compiler: fix escapes due to nil checks
Some tests get bigger, most get smaller. However, all tested driver examples get smaller in size showing that this is a good change in the real world.
Этот коммит содержится в:
родитель
cd8471acae
коммит
d653088cbe
4 изменённых файлов: 46 добавлений и 2 удалений
|
@ -115,8 +115,23 @@ func (c *Compiler) emitNilCheck(frame *Frame, ptr llvm.Value, blockPrefix string
|
||||||
frame.blockExits[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
frame.blockExits[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||||
|
|
||||||
// Compare against nil.
|
// Compare against nil.
|
||||||
nilptr := llvm.ConstPointerNull(ptr.Type())
|
var isnil llvm.Value
|
||||||
isnil := c.builder.CreateICmp(llvm.IntEQ, ptr, nilptr, "")
|
if ptr.Type().PointerAddressSpace() == 0 {
|
||||||
|
// Do the nil check using the isnil builtin, which marks the parameter
|
||||||
|
// as nocapture.
|
||||||
|
// The reason it has to go through a builtin, is that a regular icmp
|
||||||
|
// instruction may capture the pointer in LLVM semantics, see
|
||||||
|
// https://reviews.llvm.org/D60047 for details. Pointer capturing
|
||||||
|
// unfortunately breaks escape analysis, so we use this trick to let the
|
||||||
|
// functionattr pass know that this pointer doesn't really escape.
|
||||||
|
ptr = c.builder.CreateBitCast(ptr, c.i8ptrType, "")
|
||||||
|
isnil = c.createRuntimeCall("isnil", []llvm.Value{ptr}, "")
|
||||||
|
} else {
|
||||||
|
// Do the nil check using a regular icmp. This can happen with function
|
||||||
|
// pointers on AVR, which don't benefit from escape analysis anyway.
|
||||||
|
nilptr := llvm.ConstPointerNull(ptr.Type())
|
||||||
|
isnil = c.builder.CreateICmp(llvm.IntEQ, ptr, nilptr, "")
|
||||||
|
}
|
||||||
c.builder.CreateCondBr(isnil, faultBlock, nextBlock)
|
c.builder.CreateCondBr(isnil, faultBlock, nextBlock)
|
||||||
|
|
||||||
// Fail: this is a nil pointer, exit with a panic.
|
// Fail: this is a nil pointer, exit with a panic.
|
||||||
|
|
|
@ -379,6 +379,11 @@ func (c *Compiler) Compile(mainPath string) error {
|
||||||
c.mod.NamedFunction("runtime.alloc").AddAttributeAtIndex(0, attr)
|
c.mod.NamedFunction("runtime.alloc").AddAttributeAtIndex(0, attr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See emitNilCheck in asserts.go.
|
||||||
|
attrKind := llvm.AttributeKindID("nocapture")
|
||||||
|
attr := c.ctx.CreateEnumAttribute(attrKind, 0)
|
||||||
|
c.mod.NamedFunction("runtime.isnil").AddAttributeAtIndex(1, attr)
|
||||||
|
|
||||||
// see: https://reviews.llvm.org/D18355
|
// see: https://reviews.llvm.org/D18355
|
||||||
if c.Debug {
|
if c.Debug {
|
||||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||||
|
|
|
@ -53,6 +53,22 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||||
c.OptimizeAllocs()
|
c.OptimizeAllocs()
|
||||||
c.OptimizeStringToBytes()
|
c.OptimizeStringToBytes()
|
||||||
|
|
||||||
|
// Lower runtime.isnil calls to regular nil comparisons.
|
||||||
|
isnil := c.mod.NamedFunction("runtime.isnil")
|
||||||
|
if !isnil.IsNil() {
|
||||||
|
for _, use := range getUses(isnil) {
|
||||||
|
c.builder.SetInsertPointBefore(use)
|
||||||
|
ptr := use.Operand(0)
|
||||||
|
if !ptr.IsABitCastInst().IsNil() {
|
||||||
|
ptr = ptr.Operand(0)
|
||||||
|
}
|
||||||
|
nilptr := llvm.ConstPointerNull(ptr.Type())
|
||||||
|
icmp := c.builder.CreateICmp(llvm.IntEQ, ptr, nilptr, "")
|
||||||
|
use.ReplaceAllUsesWith(icmp)
|
||||||
|
use.EraseFromParentAsInstruction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err := c.LowerGoroutines()
|
err := c.LowerGoroutines()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -27,6 +27,14 @@ func _recover() interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See emitNilCheck in compiler/asserts.go.
|
||||||
|
// This function is a dummy function that has its first and only parameter
|
||||||
|
// marked 'nocapture' to work around a limitation in LLVM: a regular pointer
|
||||||
|
// comparison captures the pointer.
|
||||||
|
func isnil(ptr *uint8) bool {
|
||||||
|
return ptr == nil
|
||||||
|
}
|
||||||
|
|
||||||
// Panic when trying to dereference a nil pointer.
|
// Panic when trying to dereference a nil pointer.
|
||||||
func nilpanic() {
|
func nilpanic() {
|
||||||
runtimePanic("nil pointer dereference")
|
runtimePanic("nil pointer dereference")
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче