diff options
author | Ayke van Laethem <[email protected]> | 2021-03-18 14:55:31 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2021-03-23 14:32:33 +0100 |
commit | 51938e9d1c989edd24595b38023148e166abe2bd (patch) | |
tree | b58a4eb825637896e936493e448362d9790700ff /interp | |
parent | 19dec048b0746b21559d0bc88653d11ab61b50f0 (diff) | |
download | tinygo-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.go | 37 |
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. |