@@ -6,6 +6,7 @@ package transform
66import (
77 "sort"
88 "strconv"
9+ "strings"
910
1011 "github.com/tinygo-org/tinygo/compiler/llvmutil"
1112 "tinygo.org/x/go-llvm"
@@ -55,17 +56,30 @@ func LowerFuncValues(mod llvm.Module) {
5556 funcValueWithSignaturePtr := llvm .PointerType (mod .GetTypeByName ("runtime.funcValueWithSignature" ), 0 )
5657 signatures := map [string ]* funcSignatureInfo {}
5758 for global := mod .FirstGlobal (); ! global .IsNil (); global = llvm .NextGlobal (global ) {
58- if global .Type () != funcValueWithSignaturePtr {
59+ var sig , funcVal llvm.Value
60+ switch {
61+ case global .Type () == funcValueWithSignaturePtr :
62+ sig = llvm .ConstExtractValue (global .Initializer (), []uint32 {1 })
63+ funcVal = global
64+ case strings .HasPrefix (global .Name (), "reflect/types.type:func:{" ):
65+ sig = global
66+ default :
5967 continue
6068 }
61- sig := llvm . ConstExtractValue ( global . Initializer (), [] uint32 { 1 })
69+
6270 name := sig .Name ()
71+ var funcValueWithSignatures []llvm.Value
72+ if funcVal .IsNil () {
73+ funcValueWithSignatures = []llvm.Value {}
74+ } else {
75+ funcValueWithSignatures = []llvm.Value {funcVal }
76+ }
6377 if info , ok := signatures [name ]; ok {
64- info .funcValueWithSignatures = append (info .funcValueWithSignatures , global )
78+ info .funcValueWithSignatures = append (info .funcValueWithSignatures , funcValueWithSignatures ... )
6579 } else {
6680 signatures [name ] = & funcSignatureInfo {
6781 sig : sig ,
68- funcValueWithSignatures : []llvm. Value { global } ,
82+ funcValueWithSignatures : funcValueWithSignatures ,
6983 }
7084 }
7185 }
@@ -123,95 +137,64 @@ func LowerFuncValues(mod llvm.Module) {
123137 panic ("expected all call uses to be runtime.getFuncPtr" )
124138 }
125139 funcID := getFuncPtrCall .Operand (1 )
126- switch len (functions ) {
127- case 0 :
128- // There are no functions used in a func value that implement
129- // this signature. The only possible value is a nil value.
130- for _ , inttoptr := range getUses (getFuncPtrCall ) {
131- if inttoptr .IsAIntToPtrInst ().IsNil () {
132- panic ("expected inttoptr" )
133- }
134- nilptr := llvm .ConstPointerNull (inttoptr .Type ())
135- inttoptr .ReplaceAllUsesWith (nilptr )
136- inttoptr .EraseFromParentAsInstruction ()
140+
141+ // There are functions used in a func value that
142+ // implement this signature.
143+ // What we'll do is transform the following:
144+ // rawPtr := runtime.getFuncPtr(func.ptr)
145+ // if rawPtr == nil {
146+ // runtime.nilPanic()
147+ // }
148+ // result := rawPtr(...args, func.context)
149+ // into this:
150+ // if false {
151+ // runtime.nilPanic()
152+ // }
153+ // var result // Phi
154+ // switch fn.id {
155+ // case 0:
156+ // runtime.nilPanic()
157+ // case 1:
158+ // result = call first implementation...
159+ // case 2:
160+ // result = call second implementation...
161+ // default:
162+ // unreachable
163+ // }
164+
165+ // Remove some casts, checks, and the old call which we're going
166+ // to replace.
167+ for _ , callIntPtr := range getUses (getFuncPtrCall ) {
168+ if ! callIntPtr .IsACallInst ().IsNil () && callIntPtr .CalledValue ().Name () == "internal/task.start" {
169+ // Special case for goroutine starts.
170+ addFuncLoweringSwitch (mod , builder , funcID , callIntPtr , func (funcPtr llvm.Value , params []llvm.Value ) llvm.Value {
171+ i8ptrType := llvm .PointerType (ctx .Int8Type (), 0 )
172+ calleeValue := builder .CreatePtrToInt (funcPtr , uintptrType , "" )
173+ start := mod .NamedFunction ("internal/task.start" )
174+ builder .CreateCall (start , []llvm.Value {calleeValue , callIntPtr .Operand (1 ), llvm .Undef (i8ptrType ), llvm .ConstNull (i8ptrType )}, "" )
175+ return llvm.Value {} // void so no return value
176+ }, functions )
177+ callIntPtr .EraseFromParentAsInstruction ()
178+ continue
137179 }
138- getFuncPtrCall .EraseFromParentAsInstruction ()
139- case 1 :
140- // There is exactly one function with this signature that is
141- // used in a func value. The func value itself can be either nil
142- // or this one function.
143- builder .SetInsertPointBefore (getFuncPtrCall )
144- zero := llvm .ConstInt (uintptrType , 0 , false )
145- isnil := builder .CreateICmp (llvm .IntEQ , funcID , zero , "" )
146- funcPtrNil := llvm .ConstPointerNull (functions [0 ].funcPtr .Type ())
147- funcPtr := builder .CreateSelect (isnil , funcPtrNil , functions [0 ].funcPtr , "" )
148- for _ , inttoptr := range getUses (getFuncPtrCall ) {
149- if inttoptr .IsAIntToPtrInst ().IsNil () {
150- panic ("expected inttoptr" )
151- }
152- inttoptr .ReplaceAllUsesWith (funcPtr )
153- inttoptr .EraseFromParentAsInstruction ()
180+ if callIntPtr .IsAIntToPtrInst ().IsNil () {
181+ panic ("expected inttoptr" )
154182 }
155- getFuncPtrCall .EraseFromParentAsInstruction ()
156- default :
157- // There are multiple functions used in a func value that
158- // implement this signature.
159- // What we'll do is transform the following:
160- // rawPtr := runtime.getFuncPtr(func.ptr)
161- // if rawPtr == nil {
162- // runtime.nilPanic()
163- // }
164- // result := rawPtr(...args, func.context)
165- // into this:
166- // if false {
167- // runtime.nilPanic()
168- // }
169- // var result // Phi
170- // switch fn.id {
171- // case 0:
172- // runtime.nilPanic()
173- // case 1:
174- // result = call first implementation...
175- // case 2:
176- // result = call second implementation...
177- // default:
178- // unreachable
179- // }
180-
181- // Remove some casts, checks, and the old call which we're going
182- // to replace.
183- for _ , callIntPtr := range getUses (getFuncPtrCall ) {
184- if ! callIntPtr .IsACallInst ().IsNil () && callIntPtr .CalledValue ().Name () == "internal/task.start" {
185- // Special case for goroutine starts.
186- addFuncLoweringSwitch (mod , builder , funcID , callIntPtr , func (funcPtr llvm.Value , params []llvm.Value ) llvm.Value {
187- i8ptrType := llvm .PointerType (ctx .Int8Type (), 0 )
188- calleeValue := builder .CreatePtrToInt (funcPtr , uintptrType , "" )
189- start := mod .NamedFunction ("internal/task.start" )
190- builder .CreateCall (start , []llvm.Value {calleeValue , callIntPtr .Operand (1 ), llvm .Undef (i8ptrType ), llvm .ConstNull (i8ptrType )}, "" )
191- return llvm.Value {} // void so no return value
183+ for _ , ptrUse := range getUses (callIntPtr ) {
184+ if ! ptrUse .IsAICmpInst ().IsNil () {
185+ ptrUse .ReplaceAllUsesWith (llvm .ConstInt (ctx .Int1Type (), 0 , false ))
186+ } else if ! ptrUse .IsACallInst ().IsNil () && ptrUse .CalledValue () == callIntPtr {
187+ addFuncLoweringSwitch (mod , builder , funcID , ptrUse , func (funcPtr llvm.Value , params []llvm.Value ) llvm.Value {
188+ return builder .CreateCall (funcPtr , params , "" )
192189 }, functions )
193- callIntPtr .EraseFromParentAsInstruction ()
194- continue
195- }
196- if callIntPtr .IsAIntToPtrInst ().IsNil () {
197- panic ("expected inttoptr" )
198- }
199- for _ , ptrUse := range getUses (callIntPtr ) {
200- if ! ptrUse .IsAICmpInst ().IsNil () {
201- ptrUse .ReplaceAllUsesWith (llvm .ConstInt (ctx .Int1Type (), 0 , false ))
202- } else if ! ptrUse .IsACallInst ().IsNil () && ptrUse .CalledValue () == callIntPtr {
203- addFuncLoweringSwitch (mod , builder , funcID , ptrUse , func (funcPtr llvm.Value , params []llvm.Value ) llvm.Value {
204- return builder .CreateCall (funcPtr , params , "" )
205- }, functions )
206- } else {
207- panic ("unexpected getFuncPtrCall" )
208- }
209- ptrUse .EraseFromParentAsInstruction ()
190+ } else {
191+ panic ("unexpected getFuncPtrCall" )
210192 }
211- callIntPtr .EraseFromParentAsInstruction ()
193+ ptrUse .EraseFromParentAsInstruction ()
212194 }
213- getFuncPtrCall .EraseFromParentAsInstruction ()
195+ callIntPtr .EraseFromParentAsInstruction ()
214196 }
197+ getFuncPtrCall .EraseFromParentAsInstruction ()
215198 }
216199 }
217200}
@@ -270,13 +253,18 @@ func addFuncLoweringSwitch(mod llvm.Module, builder llvm.Builder, funcID, call l
270253 phiBlocks [i ] = bb
271254 phiValues [i ] = result
272255 }
273- // Create the PHI node so that the call result flows into the
274- // next block (after the split). This is only necessary when the
275- // call produced a value.
276256 if call .Type ().TypeKind () != llvm .VoidTypeKind {
277- builder .SetInsertPointBefore (nextBlock .FirstInstruction ())
278- phi := builder .CreatePHI (call .Type (), "" )
279- phi .AddIncoming (phiValues , phiBlocks )
280- call .ReplaceAllUsesWith (phi )
257+ if len (functions ) > 0 {
258+ // Create the PHI node so that the call result flows into the
259+ // next block (after the split). This is only necessary when the
260+ // call produced a value.
261+ builder .SetInsertPointBefore (nextBlock .FirstInstruction ())
262+ phi := builder .CreatePHI (call .Type (), "" )
263+ phi .AddIncoming (phiValues , phiBlocks )
264+ call .ReplaceAllUsesWith (phi )
265+ } else {
266+ // This is always a nil panic, so replace the call result with undef.
267+ call .ReplaceAllUsesWith (llvm .Undef (call .Type ()))
268+ }
281269 }
282270}
0 commit comments