@@ -449,8 +449,15 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
449449 if global .IsNil () {
450450 return errors .New ("global not found: " + globalName )
451451 }
452+ globalType := global .GlobalValueType ()
453+ if globalType .TypeKind () != llvm .StructTypeKind || globalType .StructName () != "runtime._string" {
454+ // Verify this is indeed a string. This is needed so
455+ // that makeGlobalsModule can just create the right
456+ // globals of string type without checking.
457+ return fmt .Errorf ("%s: not a string" , globalName )
458+ }
452459 name := global .Name ()
453- newGlobal := llvm .AddGlobal (mod , global . GlobalValueType () , name + ".tmp" )
460+ newGlobal := llvm .AddGlobal (mod , globalType , name + ".tmp" )
454461 global .ReplaceAllUsesWith (newGlobal )
455462 global .EraseFromParentAsGlobal ()
456463 newGlobal .SetName (name )
@@ -538,6 +545,15 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
538545 }
539546 }
540547
548+ // Insert values from -ldflags="-X ..." into the IR.
549+ // This is a separate module, so that the "runtime._string" type
550+ // doesn't need to match precisely. LLVM tends to rename that type
551+ // sometimes, leading to errors. But linking in a separate module
552+ // works fine. See:
553+ // https://github.com/tinygo-org/tinygo/issues/4810
554+ globalsMod := makeGlobalsModule (ctx , globalValues , machine )
555+ llvm .LinkModules (mod , globalsMod )
556+
541557 // Create runtime.initAll function that calls the runtime
542558 // initializer of each package.
543559 llvmInitFn := mod .NamedFunction ("runtime.initAll" )
@@ -590,7 +606,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
590606
591607 // Run all optimization passes, which are much more effective now
592608 // that the optimizer can see the whole program at once.
593- err := optimizeProgram (mod , config , globalValues )
609+ err := optimizeProgram (mod , config )
594610 if err != nil {
595611 return err
596612 }
@@ -1145,7 +1161,7 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
11451161// optimizeProgram runs a series of optimizations and transformations that are
11461162// needed to convert a program to its final form. Some transformations are not
11471163// optional and must be run as the compiler expects them to run.
1148- func optimizeProgram (mod llvm.Module , config * compileopts.Config , globalValues map [ string ] map [ string ] string ) error {
1164+ func optimizeProgram (mod llvm.Module , config * compileopts.Config ) error {
11491165 err := interp .Run (mod , config .Options .InterpTimeout , config .DumpSSA ())
11501166 if err != nil {
11511167 return err
@@ -1163,12 +1179,6 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config, globalValues m
11631179 }
11641180 }
11651181
1166- // Insert values from -ldflags="-X ..." into the IR.
1167- err = setGlobalValues (mod , globalValues )
1168- if err != nil {
1169- return err
1170- }
1171-
11721182 // Run most of the whole-program optimizations (including the whole
11731183 // O0/O1/O2/Os/Oz optimization pipeline).
11741184 errs := transform .Optimize (mod , config )
@@ -1182,10 +1192,19 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config, globalValues m
11821192 return nil
11831193}
11841194
1185- // setGlobalValues sets the global values from the -ldflags="-X ..." compiler
1186- // option in the given module. An error may be returned if the global is not of
1187- // the expected type.
1188- func setGlobalValues (mod llvm.Module , globals map [string ]map [string ]string ) error {
1195+ func makeGlobalsModule (ctx llvm.Context , globals map [string ]map [string ]string , machine llvm.TargetMachine ) llvm.Module {
1196+ mod := ctx .NewModule ("cmdline-globals" )
1197+ targetData := machine .CreateTargetData ()
1198+ defer targetData .Dispose ()
1199+ mod .SetDataLayout (targetData .String ())
1200+
1201+ stringType := ctx .StructCreateNamed ("runtime._string" )
1202+ uintptrType := ctx .IntType (targetData .PointerSize () * 8 )
1203+ stringType .StructSetBody ([]llvm.Type {
1204+ llvm .PointerType (ctx .Int8Type (), 0 ),
1205+ uintptrType ,
1206+ }, false )
1207+
11891208 var pkgPaths []string
11901209 for pkgPath := range globals {
11911210 pkgPaths = append (pkgPaths , pkgPath )
@@ -1201,24 +1220,6 @@ func setGlobalValues(mod llvm.Module, globals map[string]map[string]string) erro
12011220 for _ , name := range names {
12021221 value := pkg [name ]
12031222 globalName := pkgPath + "." + name
1204- global := mod .NamedGlobal (globalName )
1205- if global .IsNil () || ! global .Initializer ().IsNil () {
1206- // The global either does not exist (optimized away?) or has
1207- // some value, in which case it has already been initialized at
1208- // package init time.
1209- continue
1210- }
1211-
1212- // A strin is a {ptr, len} pair. We need these types to build the
1213- // initializer.
1214- initializerType := global .GlobalValueType ()
1215- if initializerType .TypeKind () != llvm .StructTypeKind || initializerType .StructName () == "" {
1216- return fmt .Errorf ("%s: not a string" , globalName )
1217- }
1218- elementTypes := initializerType .StructElementTypes ()
1219- if len (elementTypes ) != 2 {
1220- return fmt .Errorf ("%s: not a string" , globalName )
1221- }
12221223
12231224 // Create a buffer for the string contents.
12241225 bufInitializer := mod .Context ().ConstString (value , false )
@@ -1229,22 +1230,20 @@ func setGlobalValues(mod llvm.Module, globals map[string]map[string]string) erro
12291230 buf .SetLinkage (llvm .PrivateLinkage )
12301231
12311232 // Create the string value, which is a {ptr, len} pair.
1232- zero := llvm .ConstInt (mod .Context ().Int32Type (), 0 , false )
1233- ptr := llvm .ConstGEP (bufInitializer .Type (), buf , []llvm.Value {zero , zero })
1234- if ptr .Type () != elementTypes [0 ] {
1235- return fmt .Errorf ("%s: not a string" , globalName )
1236- }
1237- length := llvm .ConstInt (elementTypes [1 ], uint64 (len (value )), false )
1238- initializer := llvm .ConstNamedStruct (initializerType , []llvm.Value {
1239- ptr ,
1233+ length := llvm .ConstInt (uintptrType , uint64 (len (value )), false )
1234+ initializer := llvm .ConstNamedStruct (stringType , []llvm.Value {
1235+ buf ,
12401236 length ,
12411237 })
12421238
1243- // Set the initializer. No initializer should be set at this point.
1239+ // Create the string global.
1240+ global := llvm .AddGlobal (mod , stringType , globalName )
12441241 global .SetInitializer (initializer )
1242+ global .SetAlignment (targetData .PrefTypeAlignment (stringType ))
12451243 }
12461244 }
1247- return nil
1245+
1246+ return mod
12481247}
12491248
12501249// functionStackSizes keeps stack size information about a single function
0 commit comments