mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-14 16:03:41 +00:00
compiler: implement method-set based AssignableTo and Implements (#5304)
* reflect: implement method-set based AssignableTo and Implements Based on the design from #4376 by aykevl. Fixes #4277, fixes #3580. Co-authored-by: Ayke van Laethem <aykevanlaethem@gmail.com> * builder: update expected binary sizes for reflect changes * Make interface checks similar to invoke, allowing typeImplementsMethodSet and method info to be dropped when reflect is not present * Add more tests that BigGo reflect tests * Even more pruning * Add go/token and net/url to passing tests * Prune even further, I am less happy with this, though * Update size test now that we are smaller * Skip some tests * elide method lists * format, oops * fix tests * Add a panic, pull out constant to keep in sync * Add debug info * Remove code that was leftover from a previous refactor --------- Co-authored-by: Ayke van Laethem <aykevanlaethem@gmail.com>
This commit is contained in:
@@ -4,8 +4,6 @@ package transform
|
||||
// calls.
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
@@ -100,81 +98,3 @@ func OptimizeStringEqual(mod llvm.Module) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OptimizeReflectImplements optimizes the following code:
|
||||
//
|
||||
// implements := someType.Implements(someInterfaceType)
|
||||
//
|
||||
// where someType is an arbitrary reflect.Type and someInterfaceType is a
|
||||
// reflect.Type of interface kind, to the following code:
|
||||
//
|
||||
// _, implements := someType.(interfaceType)
|
||||
//
|
||||
// if the interface type is known at compile time (that is, someInterfaceType is
|
||||
// a LLVM constant aggregate). This optimization is especially important for the
|
||||
// encoding/json package, which uses this method.
|
||||
//
|
||||
// As of this writing, the (reflect.Type).Interface method has not yet been
|
||||
// implemented so this optimization is critical for the encoding/json package.
|
||||
func OptimizeReflectImplements(mod llvm.Module) {
|
||||
implementsSignature1 := mod.NamedGlobal("reflect/methods.Implements(reflect.Type) bool")
|
||||
implementsSignature2 := mod.NamedGlobal("reflect/methods.Implements(internal/reflectlite.Type) bool")
|
||||
if implementsSignature1.IsNil() && implementsSignature2.IsNil() {
|
||||
return
|
||||
}
|
||||
|
||||
builder := mod.Context().NewBuilder()
|
||||
defer builder.Dispose()
|
||||
|
||||
// Look up the (reflect.Value).Implements() method.
|
||||
var implementsFunc llvm.Value
|
||||
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
attr := fn.GetStringAttributeAtIndex(-1, "tinygo-invoke")
|
||||
if attr.IsNil() {
|
||||
continue
|
||||
}
|
||||
val := attr.GetStringValue()
|
||||
if val == "reflect/methods.Implements(reflect.Type) bool" || val == "reflect/methods.Implements(internal/reflectlite.Type) bool" {
|
||||
implementsFunc = fn
|
||||
break
|
||||
}
|
||||
}
|
||||
if implementsFunc.IsNil() {
|
||||
// Doesn't exist in the program, so nothing to do.
|
||||
return
|
||||
}
|
||||
|
||||
for _, call := range getUses(implementsFunc) {
|
||||
if call.IsACallInst().IsNil() {
|
||||
continue
|
||||
}
|
||||
interfaceType := stripPointerCasts(call.Operand(2))
|
||||
if interfaceType.IsAGlobalVariable().IsNil() {
|
||||
// Interface is unknown at compile time. This can't be optimized.
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(interfaceType.Name(), "reflect/types.type:named:") {
|
||||
// Get the underlying type.
|
||||
interfaceType = stripPointerCasts(builder.CreateExtractValue(interfaceType.Initializer(), 3, ""))
|
||||
}
|
||||
if !strings.HasPrefix(interfaceType.Name(), "reflect/types.type:interface:") {
|
||||
// This is an error. The Type passed to Implements should be of
|
||||
// interface type. Ignore it here (don't report it), it will be
|
||||
// reported at runtime.
|
||||
continue
|
||||
}
|
||||
typeAssertFunction := mod.NamedFunction(strings.TrimPrefix(interfaceType.Name(), "reflect/types.type:") + ".$typeassert")
|
||||
if typeAssertFunction.IsNil() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Replace Implements call with the type assert call.
|
||||
builder.SetInsertPointBefore(call)
|
||||
implements := builder.CreateCall(typeAssertFunction.GlobalValueType(), typeAssertFunction, []llvm.Value{
|
||||
call.Operand(0), // typecode to check
|
||||
}, "")
|
||||
call.ReplaceAllUsesWith(implements)
|
||||
call.EraseFromParentAsInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user