NSLog doesn't work properly with arm compiler

Mac OSX specific forum
User avatar
deseven
Enthusiast
Enthusiast
Posts: 367
Joined: Wed Jan 12, 2011 3:48 pm
Location: Serbia
Contact:

NSLog doesn't work properly with arm compiler

Post by deseven »

The following code works fine with x86_64 compiler:

Code: Select all

Macro CocoaAllocString(String)
  CocoaMessage(0, 0, "NSString stringWithBytes:", @String, "length:", StringByteLength(String), "encoding:", #NSUTF16LittleEndianStringEncoding)
EndMacro

ImportC ""
  NSLog(format, message)
EndImport

Define format = CocoaAllocString("Output: %S")
Define message.s = "Test: " + FormatDate("%HH:%II:%SS", Date())

NSLog(format, @message)
With x86_64 compiler it produces correct log string "Output: Test: %current_time%", with arm compiler it produces "Output: %garbage%" instead.

To see system logs, open Console app, press "Start" on top, input "PureBasic" in the search field, then run the code above.

PB 6.00 LTS @ macOS 11.6

More info in this topic.
dibor
Enthusiast
Enthusiast
Posts: 160
Joined: Wed May 20, 2020 5:19 pm
Location: The 3rd planet in the Solar System
Contact:

Re: NSLog doesn't work properly with arm compiler

Post by dibor »

Confirm.
I got on M1 Max with arm64 compiler - Output: (null)
When run x64 compiler - got - Output: Test: 00:31:57

PB 6.00 LTS @ macOS 12.6.1
Mac Studio M1Max, PB 6.12 Arm64 and x64.
Macbook Air M2, PB 6.12 Arm64 and x64.
Windows 10, PB 6.12 x64 and x86.
User avatar
sevny
New User
New User
Posts: 6
Joined: Wed Jan 10, 2018 2:33 pm

Re: NSLog doesn't work properly with arm compiler

Post by sevny »

Image

a macbook air m1 with ventura : tests with the pieces of code of this page
MacBookAir M1 2020 Ventura PB6LTS
User avatar
yuki
Enthusiast
Enthusiast
Posts: 101
Joined: Sat Mar 31, 2018 9:09 pm

Re: NSLog doesn't work properly with arm compiler

Post by yuki »

Not a bug but instead relying on undefined behaviour.

NSLog(NSString *format, ...) is a variadic function but you've defined it as accepting merely two parameters with: NSLog(format.i, message.i).

Because of calling convention differences between Apple's AMD64 and ARM64 platforms, we see the breakage here. It's only a coincidence that it works with AMD64 due to overlap between the typical convention and that used for variadic argument lists.

Potential solutions:
  • Bump a feature request for variadic functions, so the language/compiler can accommodate these as needed.
    • Has been requested a couple times: 1, 2.
    • Would be nice to have, so more FFI cases can be covered.
    • This would be much more work for @Fred, and require a permanent change to PB's syntax.
  • Update the VCall module to handle ARM64 + C-backend.
As a workaround (not a true solution), you can pass sufficient arguments to manually satisfy expected layout:

Code: Select all

Macro CocoaAllocString(String)
  CocoaMessage(0, 0, "NSString stringWithBytes:", @String, "length:", StringByteLength(String), "encoding:", #NSUTF16LittleEndianStringEncoding)
EndMacro

ImportC ""
  NSLog(format, message)
EndImport

Define format = CocoaAllocString("Output: %S")
Define message.s = "Test: " + FormatDate("%HH:%II:%SS", Date())

CompilerIf #PB_Compiler_Processor = #PB_Processor_Arm64
  CallFunctionFast(@NSLog(),
                   format,
                   0,
                   0,
                   0,
                   0,
                   0,
                   0,
                   0,
                   @message)
CompilerElse
  NSLog(format, @message)
CompilerEndIf
Though, a simpler workaround would almost certainly be to disregard the variadic nature entirely, and compose the string to log on PB's end (taking care to replace any "%" with "%%").
User avatar
deseven
Enthusiast
Enthusiast
Posts: 367
Joined: Wed Jan 12, 2011 3:48 pm
Location: Serbia
Contact:

Re: NSLog doesn't work properly with arm compiler

Post by deseven »

yuki wrote: Fri Mar 10, 2023 5:09 am Not a bug but instead relying on undefined behaviour.
Thank you so much for this great explanation! It makes total sense.

If I understood you correctly we can use something like that and it should work for both compilers without any additional checks:

Code: Select all

Macro CocoaAllocString(String)
  CocoaMessage(0, 0, "NSString stringWithBytes:", @String, "length:", StringByteLength(String), "encoding:", #NSUTF16LittleEndianStringEncoding)
EndMacro

ImportC ""
  NSLog(format)
EndImport

Procedure SystemLog(message.s)
  message = ReplaceString(message,"%","%%")
  Protected format = CocoaAllocString(message)
  NSLog(format)
EndProcedure

SystemLog("test 123%")
User avatar
yuki
Enthusiast
Enthusiast
Posts: 101
Joined: Sat Mar 31, 2018 9:09 pm

Re: NSLog doesn't work properly with arm compiler

Post by yuki »

deseven wrote: Fri Mar 10, 2023 10:30 am
yuki wrote: Fri Mar 10, 2023 5:09 am Not a bug but instead relying on undefined behaviour.
Thank you so much for this great explanation! It makes total sense.

If I understood you correctly we can use something like that and it should work for both compilers without any additional checks:

Code: Select all

Macro CocoaAllocString(String)
  CocoaMessage(0, 0, "NSString stringWithBytes:", @String, "length:", StringByteLength(String), "encoding:", #NSUTF16LittleEndianStringEncoding)
EndMacro

ImportC ""
  NSLog(format)
EndImport

Procedure SystemLog(message.s)
  message = ReplaceString(message,"%","%%")
  Protected format = CocoaAllocString(message)
  NSLog(format)
EndProcedure

SystemLog("test 123%")
No problem! And that's correct, though you'll want to make sure you release the intermediary NSString allocated:

Code: Select all

Procedure SystemLog(message.s)
  message = ReplaceString(message,"%","%%")
  Protected format = CocoaAllocString(message)
  NSLog(format)
  CocoaMessage(0, format, "release") ;; ← Add this line to ensure memory is freed. 
EndProcedure
User avatar
deseven
Enthusiast
Enthusiast
Posts: 367
Joined: Wed Jan 12, 2011 3:48 pm
Location: Serbia
Contact:

Re: NSLog doesn't work properly with arm compiler

Post by deseven »

Ah, right. I always forget to clean stuff created with CocoaMessage :)
Thanks.
User avatar
yuki
Enthusiast
Enthusiast
Posts: 101
Joined: Sat Mar 31, 2018 9:09 pm

Re: NSLog doesn't work properly with arm compiler

Post by yuki »

To be fair, with convenience methods like stringWithBytes:length:encoding:, they shouldn't be manually released. At least when using WindowEvent() or WaitWindowEvent() (or otherwise properly managing a release-pool manually).

For library code though, I somewhat prefer to manage the lifecycle myself, that way it works without needing a window/release-pool setup prior. So alloc, init, and release instead (or for strings, CFStringCreateWithCharacters() and CFRelease() are a bit more performant).
User avatar
mk-soft
Always Here
Always Here
Posts: 6202
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: NSLog doesn't work properly with arm compiler

Post by mk-soft »

Code: Select all

;- Make CocoaMessage threadsafe

Procedure foo() ; Make Thread
  Protected Pool
   
  Pool = CocoaMessage(0, 0, "NSAutoreleasePool new")
  
  ; Do any
  
  If Pool
    CocoaMessage(0, Pool, "release")
  EndIf
  
EndProcedure
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
sevny
New User
New User
Posts: 6
Joined: Wed Jan 10, 2018 2:33 pm

Re: NSLog doesn't work properly with arm compiler

Post by sevny »

Image
MacBookAir M1 2020 Ventura PB6LTS
Post Reply