aboutsummaryrefslogtreecommitdiffhomepage
path: root/interp
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2021-03-18 14:55:31 +0100
committerRon Evans <[email protected]>2021-03-23 14:32:33 +0100
commit51938e9d1c989edd24595b38023148e166abe2bd (patch)
treeb58a4eb825637896e936493e448362d9790700ff /interp
parent19dec048b0746b21559d0bc88653d11ab61b50f0 (diff)
downloadtinygo-51938e9d1c989edd24595b38023148e166abe2bd.tar.gz
tinygo-51938e9d1c989edd24595b38023148e166abe2bd.zip
interp: handle (reflect.Type).Elem()
The errors package has a call like this in the package initializer. This commit adds support for running it at compile time, avoiding the call at runtime. This doesn't always help (the call is already optimized away in many small programs) but it does help to shave off some binary size in larger programs. Perhaps more importantly, it will avoid a penalty in code size when the reflect package will convert reflect.Type from a regular type to an interface type.
Diffstat (limited to 'interp')
-rw-r--r--interp/interpreter.go37
1 files changed, 33 insertions, 4 deletions
diff --git a/interp/interpreter.go b/interp/interpreter.go
index afe0f2463..0c522ef5d 100644
--- a/interp/interpreter.go
+++ b/interp/interpreter.go
@@ -155,11 +155,8 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// which case this call won't even get to this point but will
// already be emitted in initAll.
continue
- case callFn.name == "(reflect.Type).Elem" || strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet":
+ case strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet":
// These functions should be run at runtime. Specifically:
- // * (reflect.Type).Elem is a special function. It should
- // eventually be interpreted, but fall back to a runtime call
- // for now.
// * Print and panic functions are best emitted directly without
// interpreting them, otherwise we get a ton of putchar (etc.)
// calls.
@@ -280,6 +277,38 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
dstObj.buffer = dstBuf
mem.put(dst.index(), dstObj)
+ case callFn.name == "(reflect.Type).Elem":
+ if r.debug {
+ fmt.Fprintln(os.Stderr, indent+"call (reflect.rawType).elem:", operands[1:])
+ }
+ // Extract the type code global from the first parameter.
+ typecodeID := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem).Operand(0)
+
+ // Get the type class.
+ // See also: getClassAndValueFromTypeCode in transform/reflect.go.
+ typecodeName := typecodeID.Name()
+ const prefix = "reflect/types.type:"
+ if !strings.HasPrefix(typecodeName, prefix) {
+ panic("unexpected typecode name: " + typecodeName)
+ }
+ id := typecodeName[len(prefix):]
+ class := id[:strings.IndexByte(id, ':')]
+ value := id[len(class)+1:]
+ if class == "named" {
+ // Get the underlying type.
+ class = value[:strings.IndexByte(value, ':')]
+ value = value[len(class)+1:]
+ }
+
+ // Elem() is only valid for certain type classes.
+ switch class {
+ case "chan", "pointer", "slice", "array":
+ elementType := llvm.ConstExtractValue(typecodeID.Initializer(), []uint32{0})
+ uintptrType := r.mod.Context().IntType(int(mem.r.pointerSize) * 8)
+ locals[inst.localIndex] = r.getValue(llvm.ConstPtrToInt(elementType, uintptrType))
+ default:
+ return nil, mem, r.errorAt(inst, fmt.Errorf("(reflect.Type).Elem() called on %s type", class))
+ }
case callFn.name == "runtime.typeAssert":
// This function must be implemented manually as it is normally
// implemented by the interface lowering pass.