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.
Этот коммит содержится в:
родитель
539de9db9e
коммит
64e478ef36
2 изменённых файлов: 32 добавлений и 16 удалений
36
compiler.go
36
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)
|
||||
|
|
|
@ -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
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче