From 64e478ef36445605d3771fb653cbf442e1eac476 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 26 Aug 2018 23:56:35 +0200 Subject: [PATCH] Switch to 16-bit typecodes and method IDs It took Android some time to even hit the 64K limit for regular method calls, so switching to 16-bit IDs should be fine for method IDs of interfaces. At least for the time being. When this limit is ever hit, I'll think of another way, probably involving some platform-dependent interface code (e.g. microcontrollers won't need 64K of methods) or detecting the limit at build time. https://android-developers.googleblog.com/2014/12/google-play-services-and-dex-method.html Code size isn't changed, probably because the compiler optimizes away all method calls. --- compiler.go | 36 ++++++++++++++++++++++++++---------- src/runtime/interface.go | 12 ++++++------ 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/compiler.go b/compiler.go index ee7ff33e..10f938e7 100644 --- a/compiler.go +++ b/compiler.go @@ -381,8 +381,8 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error { rangeType := c.mod.GetTypeByName("runtime.methodSetRange") for _, meta := range dynamicTypes { rangeValues := []llvm.Value{ - llvm.ConstInt(llvm.Int32Type(), uint64(startIndex), false), - llvm.ConstInt(llvm.Int32Type(), uint64(len(meta.Methods)), false), + llvm.ConstInt(llvm.Int16Type(), uint64(startIndex), false), + llvm.ConstInt(llvm.Int16Type(), uint64(len(meta.Methods)), false), } rangeValue := llvm.ConstNamedStruct(rangeType, rangeValues) ranges = append(ranges, rangeValue) @@ -394,12 +394,16 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error { fn := llvm.ConstBitCast(f.llvmFn, c.i8ptrType) funcPointers = append(funcPointers, fn) signatureNum := c.ir.MethodNum(method.Obj().(*types.Func)) - signature := llvm.ConstInt(llvm.Int32Type(), uint64(signatureNum), false) + signature := llvm.ConstInt(llvm.Int16Type(), uint64(signatureNum), false) signatures = append(signatures, signature) } startIndex += len(meta.Methods) } + if len(ranges) >= 1<<16 { + return errors.New("method call numbers do not fit in a 16-bit integer") + } + // Replace the pre-created arrays with the generated arrays. rangeArray := llvm.ConstArray(rangeType, ranges) rangeArrayNewGlobal := llvm.AddGlobal(c.mod, rangeArray.Type(), "runtime.methodSetRanges.tmp") @@ -417,7 +421,7 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error { funcArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(funcArrayNewGlobal, funcArrayOldGlobal.Type())) funcArrayOldGlobal.EraseFromParentAsGlobal() funcArrayNewGlobal.SetName("runtime.methodSetFunctions") - signatureArray := llvm.ConstArray(llvm.Int32Type(), signatures) + signatureArray := llvm.ConstArray(llvm.Int16Type(), signatures) signatureArrayNewGlobal := llvm.AddGlobal(c.mod, signatureArray.Type(), "runtime.methodSetSignatures.tmp") signatureArrayNewGlobal.SetInitializer(signatureArray) signatureArrayNewGlobal.SetLinkage(llvm.PrivateLinkage) @@ -426,7 +430,7 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error { signatureArrayOldGlobal.EraseFromParentAsGlobal() signatureArrayNewGlobal.SetName("runtime.methodSetSignatures") - c.mod.NamedGlobal("runtime.firstInterfaceNum").SetInitializer(llvm.ConstInt(llvm.Int32Type(), uint64(c.ir.FirstDynamicType()), false)) + c.mod.NamedGlobal("runtime.firstInterfaceNum").SetInitializer(llvm.ConstInt(llvm.Int16Type(), uint64(c.ir.FirstDynamicType()), false)) return nil } @@ -746,8 +750,11 @@ func (c *Compiler) getInterpretedValue(value Value) (llvm.Value, error) { if !ok { panic("interface number is unknown") } + if itfTypeNum >= 1<<16 { + return llvm.Value{}, errors.New("interface typecodes do not fit in a 16-bit integer") + } fields := []llvm.Value{ - llvm.ConstInt(llvm.Int32Type(), uint64(itfTypeNum), false), + llvm.ConstInt(llvm.Int16Type(), uint64(itfTypeNum), false), llvm.Undef(c.i8ptrType), } itf := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._interface"), fields) @@ -1334,7 +1341,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l } values := []llvm.Value{ itf, - llvm.ConstInt(llvm.Int32Type(), uint64(c.ir.MethodNum(instr.Method)), false), + llvm.ConstInt(llvm.Int16Type(), uint64(c.ir.MethodNum(instr.Method)), false), } fn := c.builder.CreateCall(c.mod.NamedFunction("runtime.itfmethod"), values, "invoke.func") fnCast := c.builder.CreateBitCast(fn, llvmFnType, "invoke.func.cast") @@ -1609,7 +1616,10 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { } } itfTypeNum, _ := c.ir.TypeNum(expr.X.Type()) - itf := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._interface"), []llvm.Value{llvm.ConstInt(llvm.Int32Type(), uint64(itfTypeNum), false), llvm.Undef(c.i8ptrType)}) + if itfTypeNum >= 1<<16 { + return llvm.Value{}, errors.New("interface typecodes do not fit in a 16-bit integer") + } + itf := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._interface"), []llvm.Value{llvm.ConstInt(llvm.Int16Type(), uint64(itfTypeNum), false), llvm.Undef(c.i8ptrType)}) itf = c.builder.CreateInsertValue(itf, itfValue, 1, "") return itf, nil case *ssa.MakeMap: @@ -1730,6 +1740,9 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { // Static analysis has determined this type assert will never apply. return llvm.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.ConstInt(llvm.Int1Type(), 0, false)}, false), nil } + if assertedTypeNum >= 1<<16 { + return llvm.Value{}, errors.New("interface typecodes do not fit in a 16-bit integer") + } actualTypeNum := c.builder.CreateExtractValue(itf, 0, "interface.type") valuePtr := c.builder.CreateExtractValue(itf, 1, "interface.value") var value llvm.Value @@ -1758,7 +1771,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { } // TODO: for interfaces, check whether the type implements the // interface. - commaOk := c.builder.CreateICmp(llvm.IntEQ, llvm.ConstInt(llvm.Int32Type(), uint64(assertedTypeNum), false), actualTypeNum, "") + commaOk := c.builder.CreateICmp(llvm.IntEQ, llvm.ConstInt(llvm.Int16Type(), uint64(assertedTypeNum), false), actualTypeNum, "") tuple := llvm.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.Undef(llvm.Int1Type())}, false) // create empty tuple tuple = c.builder.CreateInsertValue(tuple, value, 0, "") // insert value tuple = c.builder.CreateInsertValue(tuple, commaOk, 1, "") // insert 'comma ok' boolean @@ -1933,11 +1946,14 @@ func (c *Compiler) parseConst(expr *ssa.Const) (llvm.Value, error) { return llvm.Value{}, errors.New("non-nil interface constant") } itfTypeNum, ok := c.ir.TypeNum(expr.Type()) + if itfTypeNum >= 1<<16 { + return llvm.Value{}, errors.New("interface typecodes do not fit in a 16-bit integer") + } if !ok { panic("interface number is unknown") } fields := []llvm.Value{ - llvm.ConstInt(llvm.Int32Type(), uint64(itfTypeNum), false), + llvm.ConstInt(llvm.Int16Type(), uint64(itfTypeNum), false), llvm.Undef(c.i8ptrType), } itf := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._interface"), fields) diff --git a/src/runtime/interface.go b/src/runtime/interface.go index 2b494bb6..81e1c8a4 100644 --- a/src/runtime/interface.go +++ b/src/runtime/interface.go @@ -21,15 +21,15 @@ package runtime // the mapping from uniqued signature to function pointer. type _interface struct { - typecode uint32 + typecode uint16 value *uint8 } // This struct indicates the range of methods in the methodSetSignatures and // methodSetFunctions arrays that belong to this named type. type methodSetRange struct { - index uint32 // start index into interfaceSignatures and interfaceFunctions - length uint32 // number of methods + index uint16 // start index into interfaceSignatures and interfaceFunctions + length uint16 // number of methods } // Global constants that will be set by the compiler. The arrays are of size 0, @@ -37,14 +37,14 @@ type methodSetRange struct { // in. var ( methodSetRanges [0]methodSetRange // indexes into methodSetSignatures and methodSetFunctions - methodSetSignatures [0]uint32 // uniqued method ID + methodSetSignatures [0]uint16 // uniqued method ID methodSetFunctions [0]*uint8 // function pointer of method - firstInterfaceNum uint32 // the lowest typecode that has at least one method + firstInterfaceNum uint16 // the lowest typecode that has at least one method ) // Get the function pointer for the method on the interface. // This is a compiler intrinsic. -func itfmethod(itf _interface, method uint32) *uint8 { +func itfmethod(itf _interface, method uint16) *uint8 { // This function doesn't do bounds checking as the supplied method must be // in the list of signatures. The compiler will only emit runtime.itfmethod // calls when the method actually exists on this interface (proven by the