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:
Ayke van Laethem
2021-03-24 16:07:44 +01:00
committed by Ron Evans
parent c5ec955081
commit bcce296ca3
12 changed files with 242 additions and 8 deletions
+99 -1
View File
@@ -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()
}
}
}