From b44d41d9ec00edf7331655d930f7679fc86d8c1c Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 28 Mar 2021 15:08:01 +0200 Subject: [PATCH] compiler: fix "fragment covers entire variable" bug This bug could sometimes be triggered by syscall/js code it seems. But it's a generic bug, not specific to WebAssembly. --- compiler/calls.go | 32 ++++++++++++++++++++++---------- compiler/compiler.go | 4 ++-- compiler/func.go | 4 ++-- compiler/interface.go | 2 +- compiler/symbol.go | 2 +- testdata/calls.go | 17 +++++++++++++++++ 6 files changed, 45 insertions(+), 16 deletions(-) diff --git a/compiler/calls.go b/compiler/calls.go index 87eab604..81842dc6 100644 --- a/compiler/calls.go +++ b/compiler/calls.go @@ -58,10 +58,10 @@ func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm // Expand an argument type to a list that can be used in a function call // parameter list. -func expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo { +func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo { switch t.TypeKind() { case llvm.StructTypeKind: - fieldInfos := flattenAggregateType(t, name, goType) + fieldInfos := c.flattenAggregateType(t, name, goType) if len(fieldInfos) <= maxFieldsPerParam { return fieldInfos } else { @@ -105,7 +105,7 @@ func (b *builder) expandFormalParamOffsets(t llvm.Type) []uint64 { func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value { switch v.Type().TypeKind() { case llvm.StructTypeKind: - fieldInfos := flattenAggregateType(v.Type(), "", nil) + fieldInfos := b.flattenAggregateType(v.Type(), "", nil) if len(fieldInfos) <= maxFieldsPerParam { fields := b.flattenAggregate(v) if len(fields) != len(fieldInfos) { @@ -124,12 +124,15 @@ func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value { // Try to flatten a struct type to a list of types. Returns a 1-element slice // with the passed in type if this is not possible. -func flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo { +func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo { typeFlags := getTypeFlags(goType) switch t.TypeKind() { case llvm.StructTypeKind: - paramInfos := make([]paramInfo, 0, t.StructElementTypesCount()) + var paramInfos []paramInfo for i, subfield := range t.StructElementTypes() { + if c.targetData.TypeAllocSize(subfield) == 0 { + continue + } suffix := strconv.Itoa(i) if goType != nil { // Try to come up with a good suffix for this struct field, @@ -152,7 +155,7 @@ func flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramIn suffix = []string{"context", "funcptr"}[i] } } - subInfos := flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i)) + subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i)) for i := range subInfos { subInfos[i].flags |= typeFlags } @@ -218,8 +221,11 @@ func extractSubfield(t types.Type, field int) types.Type { func (c *compilerContext) flattenAggregateTypeOffsets(t llvm.Type) []uint64 { switch t.TypeKind() { case llvm.StructTypeKind: - fields := make([]uint64, 0, t.StructElementTypesCount()) + var fields []uint64 for fieldIndex, field := range t.StructElementTypes() { + if c.targetData.TypeAllocSize(field) == 0 { + continue + } suboffsets := c.flattenAggregateTypeOffsets(field) offset := c.targetData.ElementOffset(t, fieldIndex) for i := range suboffsets { @@ -238,8 +244,11 @@ func (c *compilerContext) flattenAggregateTypeOffsets(t llvm.Type) []uint64 { func (b *builder) flattenAggregate(v llvm.Value) []llvm.Value { switch v.Type().TypeKind() { case llvm.StructTypeKind: - fields := make([]llvm.Value, 0, v.Type().StructElementTypesCount()) - for i := range v.Type().StructElementTypes() { + var fields []llvm.Value + for i, field := range v.Type().StructElementTypes() { + if b.targetData.TypeAllocSize(field) == 0 { + continue + } subfield := b.CreateExtractValue(v, i, "") subfields := b.flattenAggregate(subfield) fields = append(fields, subfields...) @@ -266,10 +275,13 @@ func (b *builder) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Val func (b *builder) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) { switch t.TypeKind() { case llvm.StructTypeKind: - flattened := flattenAggregateType(t, "", nil) + flattened := b.flattenAggregateType(t, "", nil) if len(flattened) <= maxFieldsPerParam { value := llvm.ConstNull(t) for i, subtyp := range t.StructElementTypes() { + if b.targetData.TypeAllocSize(subtyp) == 0 { + continue + } structField, remaining := b.collapseFormalParamInternal(subtyp, fields) fields = remaining value = b.CreateInsertValue(value, structField, i, "") diff --git a/compiler/compiler.go b/compiler/compiler.go index d012724b..29e6e0a9 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -23,7 +23,7 @@ import ( // Version of the compiler pacakge. Must be incremented each time the compiler // package changes in a way that affects the generated LLVM module. // This version is independent of the TinyGo version number. -const Version = 5 // last change: add method set to interface types +const Version = 6 // last change: fix issue 1304 func init() { llvm.InitializeAllTargets() @@ -829,7 +829,7 @@ func (b *builder) createFunction() { for _, param := range b.fn.Params { llvmType := b.getLLVMType(param.Type()) fields := make([]llvm.Value, 0, 1) - for _, info := range expandFormalParamType(llvmType, param.Name(), param.Type()) { + for _, info := range b.expandFormalParamType(llvmType, param.Name(), param.Type()) { param := b.llvmFn.Param(llvmParamIndex) param.SetName(info.name) fields = append(fields, param) diff --git a/compiler/func.go b/compiler/func.go index 31162c1d..720cbe1f 100644 --- a/compiler/func.go +++ b/compiler/func.go @@ -124,13 +124,13 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type { // The receiver is not an interface, but a i8* type. recv = c.i8ptrType } - for _, info := range expandFormalParamType(recv, "", nil) { + for _, info := range c.expandFormalParamType(recv, "", nil) { paramTypes = append(paramTypes, info.llvmType) } } for i := 0; i < typ.Params().Len(); i++ { subType := c.getLLVMType(typ.Params().At(i).Type()) - for _, info := range expandFormalParamType(subType, "", nil) { + for _, info := range c.expandFormalParamType(subType, "", nil) { paramTypes = append(paramTypes, info.llvmType) } } diff --git a/compiler/interface.go b/compiler/interface.go index dae09857..6cca7876 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -456,7 +456,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv // Get the expanded receiver type. receiverType := c.getLLVMType(fn.Signature.Recv().Type()) var expandedReceiverType []llvm.Type - for _, info := range expandFormalParamType(receiverType, "", nil) { + for _, info := range c.expandFormalParamType(receiverType, "", nil) { expandedReceiverType = append(expandedReceiverType, info.llvmType) } diff --git a/compiler/symbol.go b/compiler/symbol.go index 9857c881..5b44c20f 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -74,7 +74,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value { var paramInfos []paramInfo for _, param := range getParams(fn.Signature) { paramType := c.getLLVMType(param.Type()) - paramFragmentInfos := expandFormalParamType(paramType, param.Name(), param.Type()) + paramFragmentInfos := c.expandFormalParamType(paramType, param.Name(), param.Type()) paramInfos = append(paramInfos, paramFragmentInfos...) } diff --git a/testdata/calls.go b/testdata/calls.go index d1c80dd0..94e6c0df 100644 --- a/testdata/calls.go +++ b/testdata/calls.go @@ -74,6 +74,14 @@ func main() { //Test deferred builtins testDeferBuiltinClose() testDeferBuiltinDelete() + + // Check for issue 1304. + // There are two fields in this struct, one of which is zero-length so the + // other covers the entire struct. This led to a verification error for + // debug info, which used DW_OP_LLVM_fragment for a field that practically + // covered the entire variable. + var x issue1304 + x.call() } func runFunc(f func(int), arg int) { @@ -211,3 +219,12 @@ func foo(bar *Bar) error { return nil } + +type issue1304 struct { + a [0]int // zero-length field + b int // field 'b' covers entire struct +} + +func (x issue1304) call() { + // nothing to do +}