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.
Этот коммит содержится в:
Ayke van Laethem 2018-08-26 23:56:35 +02:00
родитель 539de9db9e
коммит 64e478ef36
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED
2 изменённых файлов: 32 добавлений и 16 удалений

Просмотреть файл

@ -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