From 9a3d0683b34f9f030e1d0323bea849d56054cdec Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Thu, 25 Apr 2019 11:21:14 +0200 Subject: [PATCH] compiler: mark all GEPs as inbounds In Go, it is not possible to construct pointers that are out of bounds (and not null), so let LLVM know about this fact. This leads to a significant code size reduction, around 3% in many cases. --- compiler/compiler.go | 22 +++++++++++++--------- compiler/defer.go | 10 +++++----- compiler/goroutine-lowering.go | 6 +++--- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index e729ca1e7..397cb6614 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -1324,7 +1324,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(c.ctx.Int32Type(), uint64(expr.Field), false), } - return c.builder.CreateGEP(val, indices, ""), nil + return c.builder.CreateInBoundsGEP(val, indices, ""), nil } case *ssa.Function: panic("function is not an expression") @@ -1344,7 +1344,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { alloca := c.builder.CreateAlloca(array.Type(), "index.alloca") c.builder.CreateStore(array, alloca) zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) - ptr := c.builder.CreateGEP(alloca, []llvm.Value{zero, index}, "index.gep") + ptr := c.builder.CreateInBoundsGEP(alloca, []llvm.Value{zero, index}, "index.gep") return c.builder.CreateLoad(ptr, "index.load"), nil case *ssa.IndexAddr: val := c.getValue(frame, expr.X) @@ -1385,9 +1385,9 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { llvm.ConstInt(c.ctx.Int32Type(), 0, false), index, } - return c.builder.CreateGEP(bufptr, indices, ""), nil + return c.builder.CreateInBoundsGEP(bufptr, indices, ""), nil case *types.Slice: - return c.builder.CreateGEP(bufptr, []llvm.Value{index}, ""), nil + return c.builder.CreateInBoundsGEP(bufptr, []llvm.Value{index}, ""), nil default: panic("unreachable") } @@ -1407,7 +1407,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { // Lookup byte buf := c.builder.CreateExtractValue(value, 0, "") - bufPtr := c.builder.CreateGEP(buf, []llvm.Value{index}, "") + bufPtr := c.builder.CreateInBoundsGEP(buf, []llvm.Value{index}, "") return c.builder.CreateLoad(bufPtr, ""), nil case *types.Map: valueType := expr.Type() @@ -1608,7 +1608,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { } sliceLen := c.builder.CreateSub(high, low, "slice.len") - slicePtr := c.builder.CreateGEP(value, indices, "slice.ptr") + slicePtr := c.builder.CreateInBoundsGEP(value, indices, "slice.ptr") sliceCap := c.builder.CreateSub(llvmLen, low, "slice.cap") slice := c.ctx.ConstStruct([]llvm.Value{ @@ -1641,7 +1641,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { high = c.builder.CreateTrunc(high, c.uintptrType, "") } - newPtr := c.builder.CreateGEP(oldPtr, []llvm.Value{low}, "") + newPtr := c.builder.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "") newLen := c.builder.CreateSub(high, low, "") newCap := c.builder.CreateSub(oldCap, low, "") slice := c.ctx.ConstStruct([]llvm.Value{ @@ -1676,7 +1676,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { high = c.builder.CreateTrunc(high, c.uintptrType, "") } - newPtr := c.builder.CreateGEP(oldPtr, []llvm.Value{low}, "") + newPtr := c.builder.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "") newLen := c.builder.CreateSub(high, low, "") str := llvm.Undef(c.mod.GetTypeByName("runtime._string")) str = c.builder.CreateInsertValue(str, newPtr, 0, "") @@ -2119,7 +2119,11 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value, p // This pointer can be calculated from the original // ptrtoint instruction with a GEP. The leftover inttoptr // instruction is trivial to optimize away. - return c.builder.CreateGEP(origptr, []llvm.Value{index}, ""), nil + // Making it an in bounds GEP even though it's easy to + // create a GEP that is not in bounds. However, we're + // talking about unsafe code here so the programmer has to + // be careful anyway. + return c.builder.CreateInBoundsGEP(origptr, []llvm.Value{index}, ""), nil } } } diff --git a/compiler/defer.go b/compiler/defer.go index 016c1c73f..1246f03a0 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -172,13 +172,13 @@ func (c *Compiler) emitRunDefers(frame *Frame) { // stack = stack.next // switch stack.callback { c.builder.SetInsertPointAtEnd(loop) - nextStackGEP := c.builder.CreateGEP(deferData, []llvm.Value{ + nextStackGEP := c.builder.CreateInBoundsGEP(deferData, []llvm.Value{ llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(c.ctx.Int32Type(), 1, false), // .next field }, "stack.next.gep") nextStack := c.builder.CreateLoad(nextStackGEP, "stack.next") c.builder.CreateStore(nextStack, frame.deferPtr) - gep := c.builder.CreateGEP(deferData, []llvm.Value{ + gep := c.builder.CreateInBoundsGEP(deferData, []llvm.Value{ llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(c.ctx.Int32Type(), 0, false), // .callback field }, "callback.gep") @@ -211,7 +211,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) { forwardParams := []llvm.Value{} zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) for i := 2; i < len(valueTypes); i++ { - gep := c.builder.CreateGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false)}, "gep") + gep := c.builder.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false)}, "gep") forwardParam := c.builder.CreateLoad(gep, "param") forwardParams = append(forwardParams, forwardParam) } @@ -242,7 +242,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) { forwardParams := []llvm.Value{} zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) for i := range callback.Params { - gep := c.builder.CreateGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i+2), false)}, "gep") + gep := c.builder.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i+2), false)}, "gep") forwardParam := c.builder.CreateLoad(gep, "param") forwardParams = append(forwardParams, forwardParam) } @@ -273,7 +273,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) { forwardParams := []llvm.Value{} zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) for i := 2; i < len(valueTypes); i++ { - gep := c.builder.CreateGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false)}, "") + gep := c.builder.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false)}, "") forwardParam := c.builder.CreateLoad(gep, "param") forwardParams = append(forwardParams, forwardParam) } diff --git a/compiler/goroutine-lowering.go b/compiler/goroutine-lowering.go index fa4983444..aae344d85 100644 --- a/compiler/goroutine-lowering.go +++ b/compiler/goroutine-lowering.go @@ -485,7 +485,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) { llvm.ConstInt(c.ctx.Int1Type(), 0, false), }, "task.promise.raw") promise := c.builder.CreateBitCast(promiseRaw, llvm.PointerType(promiseType, 0), "task.promise") - dataPtr := c.builder.CreateGEP(promise, []llvm.Value{ + dataPtr := c.builder.CreateInBoundsGEP(promise, []llvm.Value{ llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(c.ctx.Int32Type(), 2, false), }, "task.promise.data") @@ -546,13 +546,13 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) { llvm.ConstInt(c.ctx.Int1Type(), 0, false), }, "task.promise.raw") promise := c.builder.CreateBitCast(promiseRaw, llvm.PointerType(promiseType, 0), "task.promise") - dataPtr := c.builder.CreateGEP(promise, []llvm.Value{ + dataPtr := c.builder.CreateInBoundsGEP(promise, []llvm.Value{ llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(c.ctx.Int32Type(), 2, false), }, "task.promise.data") valueAlloca.ReplaceAllUsesWith(c.builder.CreateBitCast(dataPtr, valueAlloca.Type(), "")) valueAlloca.EraseFromParentAsInstruction() - commaOkPtr := c.builder.CreateGEP(promise, []llvm.Value{ + commaOkPtr := c.builder.CreateInBoundsGEP(promise, []llvm.Value{ llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(c.ctx.Int32Type(), 1, false), }, "task.promise.comma-ok")