mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-15 16:33:40 +00:00
transform: optimize reflect.Type Implements() method
This commit adds a new transform that converts reflect Implements() calls to runtime.interfaceImplements. At the moment, the Implements() method is not yet implemented (how ironic) but if the value passed to Implements is known at compile time the method call can be optimized to runtime.interfaceImplements to make it a regular interface assert. This commit is the last change necessary to add basic support for the encoding/json package. The json package is certainly not yet fully supported, but some trivial objects can be converted to JSON.
This commit is contained in:
committed by
Ron Evans
parent
c5ec955081
commit
bcce296ca3
+99
-1
@@ -1,8 +1,11 @@
|
||||
package transform
|
||||
|
||||
// This file implements several small optimizations of runtime calls.
|
||||
// This file implements several small optimizations of runtime and reflect
|
||||
// calls.
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
@@ -85,3 +88,98 @@ 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) {
|
||||
implementsSignature := mod.NamedGlobal("func Implements(reflect.Type) bool")
|
||||
if implementsSignature.IsNil() {
|
||||
return
|
||||
}
|
||||
interfaceMethod := mod.NamedFunction("runtime.interfaceMethod")
|
||||
if interfaceMethod.IsNil() {
|
||||
return
|
||||
}
|
||||
interfaceImplements := mod.NamedFunction("runtime.interfaceImplements")
|
||||
if interfaceImplements.IsNil() {
|
||||
return
|
||||
}
|
||||
|
||||
builder := mod.Context().NewBuilder()
|
||||
defer builder.Dispose()
|
||||
|
||||
// Get a few useful object for use later.
|
||||
zero := llvm.ConstInt(mod.Context().Int32Type(), 0, false)
|
||||
uintptrType := mod.Context().IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
|
||||
|
||||
defer llvm.VerifyModule(mod, llvm.PrintMessageAction)
|
||||
|
||||
for _, use := range getUses(implementsSignature) {
|
||||
if use.IsACallInst().IsNil() {
|
||||
continue
|
||||
}
|
||||
if use.CalledValue() != interfaceMethod {
|
||||
continue
|
||||
}
|
||||
for _, bitcast := range getUses(use) {
|
||||
if !bitcast.IsABitCastInst().IsNil() {
|
||||
continue
|
||||
}
|
||||
for _, call := range getUses(bitcast) {
|
||||
// Try to get the interface method set.
|
||||
interfaceTypeBitCast := call.Operand(2)
|
||||
if interfaceTypeBitCast.IsAConstantExpr().IsNil() || interfaceTypeBitCast.Opcode() != llvm.BitCast {
|
||||
continue
|
||||
}
|
||||
interfaceType := interfaceTypeBitCast.Operand(0)
|
||||
if strings.HasPrefix(interfaceType.Name(), "reflect/types.type:named:") {
|
||||
// Get the underlying type.
|
||||
interfaceType = llvm.ConstExtractValue(interfaceType.Initializer(), []uint32{0})
|
||||
}
|
||||
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
|
||||
}
|
||||
if interfaceType.IsAGlobalVariable().IsNil() {
|
||||
// Interface is unknown at compile time. This can't be
|
||||
// optimized.
|
||||
continue
|
||||
}
|
||||
// Get the 'references' field of the runtime.typecodeID, which
|
||||
// is a bitcast of an interface method set.
|
||||
interfaceMethodSet := llvm.ConstExtractValue(interfaceType.Initializer(), []uint32{0}).Operand(0)
|
||||
|
||||
builder.SetInsertPointBefore(call)
|
||||
implements := builder.CreateCall(interfaceImplements, []llvm.Value{
|
||||
builder.CreatePtrToInt(call.Operand(0), uintptrType, ""), // typecode to check
|
||||
llvm.ConstGEP(interfaceMethodSet, []llvm.Value{zero, zero}), // method set to check against
|
||||
llvm.Undef(llvm.PointerType(mod.Context().Int8Type(), 0)),
|
||||
llvm.Undef(llvm.PointerType(mod.Context().Int8Type(), 0)),
|
||||
}, "")
|
||||
call.ReplaceAllUsesWith(implements)
|
||||
call.EraseFromParentAsInstruction()
|
||||
}
|
||||
if !hasUses(bitcast) {
|
||||
bitcast.EraseFromParentAsInstruction()
|
||||
}
|
||||
}
|
||||
if !hasUses(use) {
|
||||
use.EraseFromParentAsInstruction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user