mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-10 14:03:39 +00:00
compiler: do not check for impossible type asserts
Previously there was code to avoid impossible type asserts but it wasn't
great and in fact was too aggressive when combined with reflection.
This commit improves this by checking all types that exist in the
program that may appear in an interface (even struct fields and the
like) but without creating runtime.typecodeID objects with the type
assert. This has two advantages:
* As mentioned, it optimizes impossible type asserts away.
* It allows methods on types that were only asserted on (in
runtime.typeAssert) but never used in an interface to be optimized
away using GlobalDCE. This may have a cascading effect so that other
parts of the code can be further optimized.
This sometimes massively improves code size and mostly negates the code
size regression of the previous commit.
This commit is contained in:
committed by
Ron Evans
parent
bbb2909283
commit
19dec048b0
@@ -194,8 +194,11 @@ func (p *lowerInterfacesPass) run() error {
|
||||
typeAssertUses := getUses(typeAssert)
|
||||
for _, use := range typeAssertUses {
|
||||
typecode := use.Operand(1)
|
||||
name := typecode.Name()
|
||||
p.types[name].countTypeAsserts++
|
||||
name := typecode.Name() // name with $id suffix
|
||||
name = name[:len(name)-len("$id")] // remove $id suffix
|
||||
if t, ok := p.types[name]; ok {
|
||||
t.countTypeAsserts++
|
||||
}
|
||||
}
|
||||
|
||||
// Find all interface method calls.
|
||||
@@ -371,13 +374,27 @@ func (p *lowerInterfacesPass) run() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Replace each type assert with an actual type comparison.
|
||||
// Replace each type assert with an actual type comparison or (if the type
|
||||
// assert is impossible) the constant false.
|
||||
llvmFalse := llvm.ConstInt(p.ctx.Int1Type(), 0, false)
|
||||
for _, use := range typeAssertUses {
|
||||
actualType := use.Operand(0)
|
||||
assertedTypeGlobal := use.Operand(1)
|
||||
p.builder.SetInsertPointBefore(use)
|
||||
commaOk := p.builder.CreateICmp(llvm.IntEQ, llvm.ConstPtrToInt(assertedTypeGlobal, p.uintptrType), actualType, "typeassert.ok")
|
||||
use.ReplaceAllUsesWith(commaOk)
|
||||
name := use.Operand(1).Name() // name with $id suffix
|
||||
name = name[:len(name)-len("$id")] // remove $id suffix
|
||||
if t, ok := p.types[name]; ok {
|
||||
// The type exists in the program, so lower to a regular integer
|
||||
// comparison.
|
||||
p.builder.SetInsertPointBefore(use)
|
||||
commaOk := p.builder.CreateICmp(llvm.IntEQ, llvm.ConstPtrToInt(t.typecode, p.uintptrType), actualType, "typeassert.ok")
|
||||
use.ReplaceAllUsesWith(commaOk)
|
||||
} else {
|
||||
// The type does not exist in the program, so lower to a constant
|
||||
// false. This is trivially further optimized.
|
||||
// TODO: eventually it'll be necessary to handle reflect.PtrTo and
|
||||
// reflect.New calls which create new types not present in the
|
||||
// original program.
|
||||
use.ReplaceAllUsesWith(llvmFalse)
|
||||
}
|
||||
use.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user