compiler: simplify code around getZeroValue

The LLVM library we use does not (yet) provide a llvm.Zero (like it
provides a llvm.Undef) so we have implemented our own. However, in
theory it might return an error in some cases.

No real-world errors have been seen in a while and errors would likely
indicate a serious compiler bug anyway (not an external error), so make
it panic instead of returning an error.
Этот коммит содержится в:
Ayke van Laethem 2019-04-21 14:04:22 +02:00 коммит произвёл Ron Evans
родитель 024eceb476
коммит 6d23809218
5 изменённых файлов: 24 добавлений и 62 удалений

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

@ -123,10 +123,7 @@ func (c *Compiler) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value)
switch t.TypeKind() { switch t.TypeKind() {
case llvm.StructTypeKind: case llvm.StructTypeKind:
if len(c.flattenAggregateType(t)) <= MaxFieldsPerParam { if len(c.flattenAggregateType(t)) <= MaxFieldsPerParam {
value, err := c.getZeroValue(t) value := c.getZeroValue(t)
if err != nil {
panic("could not get zero value of struct: " + err.Error())
}
for i, subtyp := range t.StructElementTypes() { for i, subtyp := range t.StructElementTypes() {
structField, remaining := c.collapseFormalParamInternal(subtyp, fields) structField, remaining := c.collapseFormalParamInternal(subtyp, fields)
fields = remaining fields = remaining

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

@ -297,11 +297,7 @@ func (c *Compiler) Compile(mainPath string) error {
g.LLVMGlobal = global g.LLVMGlobal = global
if !g.IsExtern() { if !g.IsExtern() {
global.SetLinkage(llvm.InternalLinkage) global.SetLinkage(llvm.InternalLinkage)
initializer, err := c.getZeroValue(llvmType) global.SetInitializer(c.getZeroValue(llvmType))
if err != nil {
return err
}
global.SetInitializer(initializer)
} }
} }
@ -544,42 +540,35 @@ func (c *Compiler) getLLVMType(goType types.Type) (llvm.Type, error) {
// initializer has the same effect as setting 'zeroinitializer' on a value. // initializer has the same effect as setting 'zeroinitializer' on a value.
// Sadly, I haven't found a way to do it directly with the Go API but this works // Sadly, I haven't found a way to do it directly with the Go API but this works
// just fine. // just fine.
func (c *Compiler) getZeroValue(typ llvm.Type) (llvm.Value, error) { func (c *Compiler) getZeroValue(typ llvm.Type) llvm.Value {
switch typ.TypeKind() { switch typ.TypeKind() {
case llvm.ArrayTypeKind: case llvm.ArrayTypeKind:
subTyp := typ.ElementType() subTyp := typ.ElementType()
subVal, err := c.getZeroValue(subTyp) subVal := c.getZeroValue(subTyp)
if err != nil {
return llvm.Value{}, err
}
vals := make([]llvm.Value, typ.ArrayLength()) vals := make([]llvm.Value, typ.ArrayLength())
for i := range vals { for i := range vals {
vals[i] = subVal vals[i] = subVal
} }
return llvm.ConstArray(subTyp, vals), nil return llvm.ConstArray(subTyp, vals)
case llvm.FloatTypeKind, llvm.DoubleTypeKind: case llvm.FloatTypeKind, llvm.DoubleTypeKind:
return llvm.ConstFloat(typ, 0.0), nil return llvm.ConstFloat(typ, 0.0)
case llvm.IntegerTypeKind: case llvm.IntegerTypeKind:
return llvm.ConstInt(typ, 0, false), nil return llvm.ConstInt(typ, 0, false)
case llvm.PointerTypeKind: case llvm.PointerTypeKind:
return llvm.ConstPointerNull(typ), nil return llvm.ConstPointerNull(typ)
case llvm.StructTypeKind: case llvm.StructTypeKind:
types := typ.StructElementTypes() types := typ.StructElementTypes()
vals := make([]llvm.Value, len(types)) vals := make([]llvm.Value, len(types))
for i, subTyp := range types { for i, subTyp := range types {
val, err := c.getZeroValue(subTyp) vals[i] = c.getZeroValue(subTyp)
if err != nil {
return llvm.Value{}, err
}
vals[i] = val
} }
if typ.StructName() != "" { if typ.StructName() != "" {
return llvm.ConstNamedStruct(typ, vals), nil return llvm.ConstNamedStruct(typ, vals)
} else { } else {
return c.ctx.ConstStruct(vals, false), nil return c.ctx.ConstStruct(vals, false)
} }
default: default:
return llvm.Value{}, errors.New("todo: LLVM zero initializer: " + typ.String()) panic("unknown LLVM zero inititializer: " + typ.String())
} }
} }
@ -1010,10 +999,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
return nil return nil
} else { } else {
// Multiple return values. Put them all in a struct. // Multiple return values. Put them all in a struct.
retVal, err := c.getZeroValue(frame.fn.LLVMFn.Type().ElementType().ReturnType()) retVal := c.getZeroValue(frame.fn.LLVMFn.Type().ElementType().ReturnType())
if err != nil {
return err
}
for i, result := range instr.Results { for i, result := range instr.Results {
val, err := c.parseExpr(frame, result) val, err := c.parseExpr(frame, result)
if err != nil { if err != nil {
@ -1380,11 +1366,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
} else { } else {
buf = c.builder.CreateAlloca(typ, expr.Comment) buf = c.builder.CreateAlloca(typ, expr.Comment)
if c.targetData.TypeAllocSize(typ) != 0 { if c.targetData.TypeAllocSize(typ) != 0 {
zero, err := c.getZeroValue(typ) c.builder.CreateStore(c.getZeroValue(typ), buf) // zero-initialize var
if err != nil {
return llvm.Value{}, err
}
c.builder.CreateStore(zero, buf) // zero-initialize var
} }
} }
return buf, nil return buf, nil
@ -1765,11 +1747,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
panic("unknown type in range: " + typ.String()) panic("unknown type in range: " + typ.String())
} }
it := c.builder.CreateAlloca(iteratorType, "range.it") it := c.builder.CreateAlloca(iteratorType, "range.it")
zero, err := c.getZeroValue(iteratorType) c.builder.CreateStore(c.getZeroValue(iteratorType), it)
if err != nil {
return llvm.Value{}, nil
}
c.builder.CreateStore(zero, it)
return it, nil return it, nil
case *ssa.Select: case *ssa.Select:
if len(expr.States) == 0 { if len(expr.States) == 0 {
@ -1936,10 +1914,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
newPtr := c.builder.CreateGEP(oldPtr, []llvm.Value{low}, "") newPtr := c.builder.CreateGEP(oldPtr, []llvm.Value{low}, "")
newLen := c.builder.CreateSub(high, low, "") newLen := c.builder.CreateSub(high, low, "")
str, err := c.getZeroValue(c.mod.GetTypeByName("runtime._string")) str := llvm.Undef(c.mod.GetTypeByName("runtime._string"))
if err != nil {
return llvm.Value{}, err
}
str = c.builder.CreateInsertValue(str, newPtr, 0, "") str = c.builder.CreateInsertValue(str, newPtr, 0, "")
str = c.builder.CreateInsertValue(str, newLen, 1, "") str = c.builder.CreateInsertValue(str, newLen, 1, "")
return str, nil return str, nil
@ -2323,7 +2298,7 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) (llvm.Value, error
if err != nil { if err != nil {
return llvm.Value{}, err return llvm.Value{}, err
} }
return c.getZeroValue(sig) return c.getZeroValue(sig), nil
case *types.Signature: case *types.Signature:
if expr.Value != nil { if expr.Value != nil {
return llvm.Value{}, errors.New("non-nil signature constant") return llvm.Value{}, errors.New("non-nil signature constant")
@ -2332,7 +2307,7 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) (llvm.Value, error
if err != nil { if err != nil {
return llvm.Value{}, err return llvm.Value{}, err
} }
return c.getZeroValue(sig) return c.getZeroValue(sig), nil
case *types.Interface: case *types.Interface:
if expr.Value != nil { if expr.Value != nil {
return llvm.Value{}, errors.New("non-nil interface constant") return llvm.Value{}, errors.New("non-nil interface constant")
@ -2378,7 +2353,7 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) (llvm.Value, error
if err != nil { if err != nil {
return llvm.Value{}, err return llvm.Value{}, err
} }
return c.getZeroValue(llvmType) return c.getZeroValue(llvmType), nil
default: default:
return llvm.Value{}, errors.New("todo: unknown constant: " + expr.String()) return llvm.Value{}, errors.New("todo: unknown constant: " + expr.String())
} }
@ -2568,7 +2543,7 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) {
valType := unop.X.Type().Underlying().(*types.Pointer).Elem() valType := unop.X.Type().Underlying().(*types.Pointer).Elem()
if c.targetData.TypeAllocSize(x.Type().ElementType()) == 0 { if c.targetData.TypeAllocSize(x.Type().ElementType()) == 0 {
// zero-length data // zero-length data
return c.getZeroValue(x.Type().ElementType()) return c.getZeroValue(x.Type().ElementType()), nil
} else if strings.HasSuffix(unop.X.String(), "$funcaddr") { } else if strings.HasSuffix(unop.X.String(), "$funcaddr") {
// CGo function pointer. The cgo part has rewritten CGo function // CGo function pointer. The cgo part has rewritten CGo function
// pointers as stub global variables of the form: // pointers as stub global variables of the form:

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

@ -136,10 +136,7 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) error {
// Make a struct out of the collected values to put in the defer frame. // Make a struct out of the collected values to put in the defer frame.
deferFrameType := c.ctx.StructType(valueTypes, false) deferFrameType := c.ctx.StructType(valueTypes, false)
deferFrame, err := c.getZeroValue(deferFrameType) deferFrame := c.getZeroValue(deferFrameType)
if err != nil {
return err
}
for i, value := range values { for i, value := range values {
deferFrame = c.builder.CreateInsertValue(deferFrame, value, i, "") deferFrame = c.builder.CreateInsertValue(deferFrame, value, i, "")
} }

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

@ -283,10 +283,6 @@ func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) (llvm.Val
if err != nil { if err != nil {
return llvm.Value{}, err return llvm.Value{}, err
} }
valueNil, err := c.getZeroValue(assertedType)
if err != nil {
return llvm.Value{}, err
}
actualTypeNum := c.builder.CreateExtractValue(itf, 0, "interface.type") actualTypeNum := c.builder.CreateExtractValue(itf, 0, "interface.type")
commaOk := llvm.Value{} commaOk := llvm.Value{}
@ -345,10 +341,7 @@ func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) (llvm.Val
valuePtrCast := c.builder.CreateBitCast(valuePtr, llvm.PointerType(assertedType, 0), "") valuePtrCast := c.builder.CreateBitCast(valuePtr, llvm.PointerType(assertedType, 0), "")
valueOk = c.builder.CreateLoad(valuePtrCast, "typeassert.value.ok") valueOk = c.builder.CreateLoad(valuePtrCast, "typeassert.value.ok")
} else if size == 0 { } else if size == 0 {
valueOk, err = c.getZeroValue(assertedType) valueOk = c.getZeroValue(assertedType)
if err != nil {
return llvm.Value{}, err
}
} else { } else {
// Value was stored directly in the interface. // Value was stored directly in the interface.
switch assertedType.TypeKind() { switch assertedType.TypeKind() {
@ -372,7 +365,7 @@ func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) (llvm.Val
// Continue after the if statement. // Continue after the if statement.
c.builder.SetInsertPointAtEnd(nextBlock) c.builder.SetInsertPointAtEnd(nextBlock)
phi := c.builder.CreatePHI(assertedType, "typeassert.value") phi := c.builder.CreatePHI(assertedType, "typeassert.value")
phi.AddIncoming([]llvm.Value{valueNil, valueOk}, []llvm.BasicBlock{prevBlock, okBlock}) phi.AddIncoming([]llvm.Value{c.getZeroValue(assertedType), valueOk}, []llvm.BasicBlock{prevBlock, okBlock})
if expr.CommaOk { if expr.CommaOk {
tuple := c.ctx.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.Undef(c.ctx.Int1Type())}, false) // create empty tuple tuple := c.ctx.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.Undef(c.ctx.Int1Type())}, false) // create empty tuple

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

@ -272,7 +272,7 @@ func (c *Compiler) OptimizeAllocs() {
sizeInWords := (size + uint64(alignment) - 1) / uint64(alignment) sizeInWords := (size + uint64(alignment) - 1) / uint64(alignment)
allocaType := llvm.ArrayType(c.ctx.IntType(alignment*8), int(sizeInWords)) allocaType := llvm.ArrayType(c.ctx.IntType(alignment*8), int(sizeInWords))
alloca := c.builder.CreateAlloca(allocaType, "stackalloc.alloca") alloca := c.builder.CreateAlloca(allocaType, "stackalloc.alloca")
zero, _ := c.getZeroValue(alloca.Type().ElementType()) zero := c.getZeroValue(alloca.Type().ElementType())
c.builder.CreateStore(zero, alloca) c.builder.CreateStore(zero, alloca)
stackalloc := c.builder.CreateBitCast(alloca, bitcast.Type(), "stackalloc") stackalloc := c.builder.CreateBitCast(alloca, bitcast.Type(), "stackalloc")
bitcast.ReplaceAllUsesWith(stackalloc) bitcast.ReplaceAllUsesWith(stackalloc)