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
|
||||
|
||||
// Compare against nil.
|
||||
nilptr := llvm.ConstPointerNull(ptr.Type())
|
||||
isnil := c.builder.CreateICmp(llvm.IntEQ, ptr, nilptr, "")
|
||||
var isnil llvm.Value
|
||||
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)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
if c.Debug {
|
||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||
|
|
|
@ -53,6 +53,22 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
|||
c.OptimizeAllocs()
|
||||
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()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -27,6 +27,14 @@ func _recover() interface{} {
|
|||
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.
|
||||
func nilpanic() {
|
||||
runtimePanic("nil pointer dereference")
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче