11//! Helpers for using Zig's LLVM Builder API to generate a shim library for the
22//! Roc interpreter that translates from the platform host API.
3+ //!
4+ //! Note: Symbol names in LLVM IR need platform-specific prefixes for macOS.
5+ //! MachO format requires underscore prefix on all C symbols.
36
47const std = @import ("std" );
58const Builder = std .zig .llvm .Builder ;
69const WipFunction = Builder .WipFunction ;
7- const builtin = @import ("builtin" ) ;
10+ const RocTarget = @import ("target.zig" ). RocTarget ;
811
912/// Represents a single entrypoint that a Roc platform host expects to call.
1013/// Each entrypoint corresponds to a specific function the host can invoke,
@@ -27,7 +30,7 @@ pub const EntryPoint = struct {
2730/// Roc platform functions will delegate to. The Roc interpreter provides
2831/// the actual implementation of this function, which acts as a dispatcher
2932/// based on the entry_idx parameter.
30- fn addRocEntrypoint (builder : * Builder ) ! Builder.Function.Index {
33+ fn addRocEntrypoint (builder : * Builder , target : RocTarget ) ! Builder.Function.Index {
3134 // Create pointer type for generic pointers (i8* in LLVM)
3235 const ptr_type = try builder .ptrType (.default );
3336
@@ -36,14 +39,14 @@ fn addRocEntrypoint(builder: *Builder) !Builder.Function.Index {
3639 const entrypoint_params = [_ ]Builder.Type { .i32 , ptr_type , ptr_type , ptr_type };
3740 const entrypoint_type = try builder .fnType (.void , & entrypoint_params , .normal );
3841
39- // Create function name with platform-specific prefix
42+ // Add underscore prefix for macOS (required for MachO symbol names)
4043 const base_name = "roc_entrypoint" ;
41- const fn_name_str = if (builtin . target .os . tag == .macos )
44+ const full_name = if (target .isMacOS () )
4245 try std .fmt .allocPrint (builder .gpa , "_{s}" , .{base_name })
4346 else
4447 try builder .gpa .dupe (u8 , base_name );
45- defer builder .gpa .free (fn_name_str );
46- const fn_name = try builder .strtabString (fn_name_str );
48+ defer builder .gpa .free (full_name );
49+ const fn_name = try builder .strtabString (full_name );
4750
4851 // Add the extern function declaration (no body)
4952 const entrypoint_fn = try builder .addFunction (entrypoint_type , fn_name , .default );
@@ -72,7 +75,7 @@ fn addRocEntrypoint(builder: *Builder) !Builder.Function.Index {
7275/// 2. The pre-built Roc interpreter to handle all calls through a single dispatch mechanism
7376/// 3. Efficient code generation since each wrapper is just a simple function call
7477/// 4. Easy addition/removal of platform functions without changing the pre-built interpreter binary which is embedded in the roc cli executable.
75- fn addRocExportedFunction (builder : * Builder , entrypoint_fn : Builder.Function.Index , name : []const u8 , entry_idx : u32 ) ! Builder.Function.Index {
78+ fn addRocExportedFunction (builder : * Builder , entrypoint_fn : Builder.Function.Index , name : []const u8 , entry_idx : u32 , target : RocTarget ) ! Builder.Function.Index {
7679 // Create pointer type for generic pointers
7780 const ptr_type = try builder .ptrType (.default );
7881
@@ -81,10 +84,11 @@ fn addRocExportedFunction(builder: *Builder, entrypoint_fn: Builder.Function.Ind
8184 const roc_fn_params = [_ ]Builder.Type { ptr_type , ptr_type , ptr_type };
8285 const roc_fn_type = try builder .fnType (.void , & roc_fn_params , .normal );
8386
84- // Create function name with roc__ prefix and platform-specific prefix
87+ // Create function name with roc__ prefix.
88+ // Add underscore prefix for macOS (required for MachO symbol names)
8589 const base_name = try std .fmt .allocPrint (builder .gpa , "roc__{s}" , .{name });
8690 defer builder .gpa .free (base_name );
87- const full_name = if (builtin . target .os . tag == .macos )
91+ const full_name = if (target .isMacOS () )
8892 try std .fmt .allocPrint (builder .gpa , "_{s}" , .{base_name })
8993 else
9094 try builder .gpa .dupe (u8 , base_name );
@@ -153,12 +157,12 @@ fn addRocExportedFunction(builder: *Builder, entrypoint_fn: Builder.Function.Ind
153157///
154158/// The generated library is then compiled using LLVM to an object file and linked with
155159/// both the host and the Roc interpreter to create a dev build executable.
156- pub fn createInterpreterShim (builder : * Builder , entrypoints : []const EntryPoint ) ! void {
160+ pub fn createInterpreterShim (builder : * Builder , entrypoints : []const EntryPoint , target : RocTarget ) ! void {
157161 // Add the extern roc_entrypoint declaration
158- const entrypoint_fn = try addRocEntrypoint (builder );
162+ const entrypoint_fn = try addRocEntrypoint (builder , target );
159163
160164 // Add each exported entrypoint function
161165 for (entrypoints ) | entry | {
162- _ = try addRocExportedFunction (builder , entrypoint_fn , entry .name , entry .idx );
166+ _ = try addRocExportedFunction (builder , entrypoint_fn , entry .name , entry .idx , target );
163167 }
164168}
0 commit comments