Page 1 of 1
NSLog doesn't work properly with arm compiler
Posted: Mon Oct 31, 2022 1:53 pm
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.
Re: NSLog doesn't work properly with arm compiler
Posted: Sat Nov 05, 2022 11:34 pm
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
Re: NSLog doesn't work properly with arm compiler
Posted: Fri Mar 10, 2023 1:43 am
by sevny
a macbook air m1 with ventura : tests with the pieces of code of this page
Re: NSLog doesn't work properly with arm compiler
Posted: Fri Mar 10, 2023 5:09 am
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 "%%").
Re: NSLog doesn't work properly with arm compiler
Posted: Fri Mar 10, 2023 10:30 am
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%")
Re: NSLog doesn't work properly with arm compiler
Posted: Fri Mar 10, 2023 10:35 am
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
Re: NSLog doesn't work properly with arm compiler
Posted: Fri Mar 10, 2023 10:40 am
by deseven
Ah, right. I always forget to clean stuff created with CocoaMessage

Thanks.
Re: NSLog doesn't work properly with arm compiler
Posted: Fri Mar 10, 2023 11:56 am
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).
Re: NSLog doesn't work properly with arm compiler
Posted: Fri Mar 10, 2023 8:00 pm
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
Re: NSLog doesn't work properly with arm compiler
Posted: Mon Mar 13, 2023 1:43 pm
by sevny