From 2cfe62f82077ba012aef55db5288985bc0c426d9 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Wed, 5 Jan 2022 20:05:11 +0100 Subject: pkg/compiler: add out_overlay field attribute --- pkg/compiler/attrs.go | 17 ++++++------ pkg/compiler/check.go | 36 ++++++++++++++++++++++--- pkg/compiler/gen.go | 57 ++++++++++++++++++++++++++++++--------- pkg/compiler/testdata/all.txt | 9 ++++++- pkg/compiler/testdata/errors.txt | 21 +++++++++++++++ pkg/compiler/testdata/errors2.txt | 12 +++++++++ pkg/compiler/types.go | 14 +++++++++- 7 files changed, 141 insertions(+), 25 deletions(-) (limited to 'pkg/compiler') diff --git a/pkg/compiler/attrs.go b/pkg/compiler/attrs.go index 5de19f884..63ba613d9 100644 --- a/pkg/compiler/attrs.go +++ b/pkg/compiler/attrs.go @@ -19,17 +19,18 @@ type attrDesc struct { } var ( - attrPacked = &attrDesc{Name: "packed"} - attrVarlen = &attrDesc{Name: "varlen"} - attrSize = &attrDesc{Name: "size", HasArg: true} - attrAlign = &attrDesc{Name: "align", HasArg: true} - attrIn = &attrDesc{Name: "in"} - attrOut = &attrDesc{Name: "out"} - attrInOut = &attrDesc{Name: "inout"} + attrPacked = &attrDesc{Name: "packed"} + attrVarlen = &attrDesc{Name: "varlen"} + attrSize = &attrDesc{Name: "size", HasArg: true} + attrAlign = &attrDesc{Name: "align", HasArg: true} + attrIn = &attrDesc{Name: "in"} + attrOut = &attrDesc{Name: "out"} + attrInOut = &attrDesc{Name: "inout"} + attrOutOverlay = &attrDesc{Name: "out_overlay"} structAttrs = makeAttrs(attrPacked, attrSize, attrAlign) unionAttrs = makeAttrs(attrVarlen, attrSize) - fieldAttrs = makeAttrs(attrIn, attrOut, attrInOut) + fieldAttrs = makeAttrs(attrIn, attrOut, attrInOut, attrOutOverlay) callAttrs = make(map[string]*attrDesc) ) diff --git a/pkg/compiler/check.go b/pkg/compiler/check.go index c5fc226d6..676b96c5c 100644 --- a/pkg/compiler/check.go +++ b/pkg/compiler/check.go @@ -182,13 +182,33 @@ func (comp *compiler) checkStructFields(n *ast.Struct, typ, name string) { if len(n.Fields) < 1 { comp.error(n.Pos, "%v %v has no fields, need at least 1 field", typ, name) } - for _, f := range n.Fields { + hasDirections, hasOutOverlay := false, false + for fieldIdx, f := range n.Fields { attrs := comp.parseAttrs(fieldAttrs, f, f.Attrs) - - if attrs[attrIn]+attrs[attrOut]+attrs[attrInOut] > 1 { + dirCount := attrs[attrIn] + attrs[attrOut] + attrs[attrInOut] + if dirCount != 0 { + hasDirections = true + } + if dirCount > 1 { _, typ, _ := f.Info() comp.error(f.Pos, "%v has multiple direction attributes", typ) } + if attrs[attrOutOverlay] > 0 { + if n.IsUnion { + _, typ, name := f.Info() + comp.error(f.Pos, "unknown %v %v attribute %v", typ, name, attrOutOverlay.Name) + } + if fieldIdx == 0 { + comp.error(f.Pos, "%v attribute must not be specified on the first field", attrOutOverlay.Name) + } + if hasOutOverlay || attrs[attrOutOverlay] > 1 { + comp.error(f.Pos, "multiple %v attributes", attrOutOverlay.Name) + } + hasOutOverlay = true + } + if hasDirections && hasOutOverlay { + comp.error(f.Pos, "mix of direction and %v attributes is not supported", attrOutOverlay.Name) + } } } @@ -301,12 +321,22 @@ func (comp *compiler) checkAttributeValues() { } // Check each field's attributes. st := decl.(*ast.Struct) + hasOutOverlay := false for _, f := range st.Fields { + isOut := hasOutOverlay for _, attr := range f.Attrs { desc := fieldAttrs[attr.Ident] if desc.CheckConsts != nil { desc.CheckConsts(comp, f, attr) } + switch attr.Ident { + case attrOutOverlay.Name: + hasOutOverlay = true + isOut = true + } + } + if isOut && comp.getTypeDesc(f.Type).CantBeOut { + comp.error(f.Pos, "%v type must not be used as output", f.Type.Ident) } } } diff --git a/pkg/compiler/gen.go b/pkg/compiler/gen.go index b4ee50a9b..78cbc54fe 100644 --- a/pkg/compiler/gen.go +++ b/pkg/compiler/gen.go @@ -66,7 +66,7 @@ func (comp *compiler) collectCallArgSizes() map[string][]uint64 { argSizes = append(argSizes, comp.ptrSize) } desc, _, _ := comp.getArgsBase(arg.Type, true) - typ := comp.genField(arg, comp.ptrSize) + typ := comp.genField(arg, comp.ptrSize, prog.DirInOut) // Ignore all types with base (const, flags). We don't have base in syscall args. // Also ignore resources and pointers because fd can be 32-bits and pointer 64-bits, // and then there is no way to fix this. @@ -124,12 +124,13 @@ func (comp *compiler) genSyscall(n *ast.Call, argSizes []uint64) *prog.Syscall { fld.SetBool(val != 0) } } + fields, _ := comp.genFieldArray(n.Args, argSizes) return &prog.Syscall{ Name: n.Name.Name, CallName: n.CallName, NR: n.NR, MissingArgs: len(argSizes) - len(n.Args), - Args: comp.genFieldArray(n.Args, argSizes), + Args: fields, Ret: ret, Attrs: attrs, } @@ -271,8 +272,15 @@ func (comp *compiler) layoutStruct(t *prog.StructType) { comp.layoutStructFields(t, varlen, attrs[attrPacked] != 0) t.TypeSize = 0 if !varlen { - for _, f := range t.Fields { - t.TypeSize += f.Size() + var size uint64 + for i, f := range t.Fields { + if i == t.OverlayField { + size = 0 + } + size += f.Size() + if t.TypeSize < size { + t.TypeSize = size + } } sizeAttr, hasSize := attrs[attrSize] if hasSize { @@ -291,9 +299,17 @@ func (comp *compiler) layoutStruct(t *prog.StructType) { func (comp *compiler) layoutStructFields(t *prog.StructType, varlen, packed bool) { var newFields []prog.Field + overlayField0 := t.OverlayField var structAlign, byteOffset, bitOffset uint64 for i, field := range t.Fields { f := field.Type + if i == overlayField0 { + // We layout fields before overlay and the overlay fields effectively as 2 independent structs. + // So if we starting overlay, add any trailign padding/finalize bitfield layout and reset state. + newFields = comp.finalizeStructFields(t, newFields, varlen, structAlign, byteOffset, bitOffset) + t.OverlayField = len(newFields) // update overlay field index after we added paddings + structAlign, byteOffset, bitOffset = 0, 0, 0 + } fieldAlign := uint64(1) if !packed { fieldAlign = f.Alignment() @@ -367,9 +383,9 @@ func (comp *compiler) finalizeStructFields(t *prog.StructType, fields []prog.Fie if bitOffset != 0 { pad := roundup(bitOffset, 8) / 8 byteOffset += pad - i := len(t.Fields) - if i != 0 && t.Fields[i-1].IsBitfield() { - setBitfieldTypeSize(t.Fields[i-1].Type, pad) + i := len(fields) + if i != 0 && fields[i-1].IsBitfield() { + setBitfieldTypeSize(fields[i-1].Type, pad) } else { fields = append(fields, genPad(pad)) } @@ -437,12 +453,26 @@ func genPad(size uint64) prog.Field { } } -func (comp *compiler) genFieldArray(fields []*ast.Field, argSizes []uint64) []prog.Field { +func (comp *compiler) genFieldArray(fields []*ast.Field, argSizes []uint64) ([]prog.Field, int) { + outOverlay := -1 + for i, f := range fields { + attrs := comp.parseAttrs(fieldAttrs, f, f.Attrs) + if attrs[attrOutOverlay] > 0 { + outOverlay = i + } + } var res []prog.Field for i, f := range fields { - res = append(res, comp.genField(f, argSizes[i])) + overlayDir := prog.DirInOut + if outOverlay != -1 { + overlayDir = prog.DirIn + if i >= outOverlay { + overlayDir = prog.DirOut + } + } + res = append(res, comp.genField(f, argSizes[i], overlayDir)) } - return res + return res, outOverlay } func (comp *compiler) genFieldDir(f *ast.Field) (prog.Dir, bool) { @@ -459,8 +489,11 @@ func (comp *compiler) genFieldDir(f *ast.Field) (prog.Dir, bool) { } } -func (comp *compiler) genField(f *ast.Field, argSize uint64) prog.Field { - dir, hasDir := comp.genFieldDir(f) +func (comp *compiler) genField(f *ast.Field, argSize uint64, overlayDir prog.Dir) prog.Field { + dir, hasDir := overlayDir, true + if overlayDir == prog.DirInOut { + dir, hasDir = comp.genFieldDir(f) + } return prog.Field{ Name: f.Name.Name, diff --git a/pkg/compiler/testdata/all.txt b/pkg/compiler/testdata/all.txt index b1adf2321..c9a02f592 100644 --- a/pkg/compiler/testdata/all.txt +++ b/pkg/compiler/testdata/all.txt @@ -287,7 +287,14 @@ s5 { f_inout1 int32[0:1] (inout) } -foo_s0(a ptr[in, s0], b ptr[in, s1], c ptr[in, s2], d ptr[in, s4], e ptr[in, s5]) +s6 { + f0 int32 + f1 r0 + f2 int32 (out_overlay) + f3 r0 +} + +foo_s0(a ptr[in, s0], b ptr[in, s1], c ptr[in, s2], d ptr[in, s4], e ptr[in, s5], f ptr[in, s6]) # Unions. diff --git a/pkg/compiler/testdata/errors.txt b/pkg/compiler/testdata/errors.txt index 65eaf8a97..6cc2e2b0f 100644 --- a/pkg/compiler/testdata/errors.txt +++ b/pkg/compiler/testdata/errors.txt @@ -391,3 +391,24 @@ struct$perfielddir { f5 int32 (out, inout) ### arg/field has multiple direction attributes f6 int32 (in, out, inout) ### arg/field has multiple direction attributes } + +struct$overlay0 { + f0 int32 (out_overlay) ### out_overlay attribute must not be specified on the first field + f1 int32 (out_overlay) ### multiple out_overlay attributes +} + +struct$overlay1 { + f0 int32 + f1 int32 (out_overlay, out_overlay) ### duplicate arg/field f1 attribute out_overlay + f2 int32 (out_overlay) ### multiple out_overlay attributes +} + +struct$overlay2 { + f0 int32 (in) + f1 int32 (out_overlay) ### mix of direction and out_overlay attributes is not supported +} + +union$overlay0 [ + f0 int32 + f1 int32 (out_overlay) ### unknown arg/field f1 attribute out_overlay +] diff --git a/pkg/compiler/testdata/errors2.txt b/pkg/compiler/testdata/errors2.txt index 111db587f..e5a51b4a9 100644 --- a/pkg/compiler/testdata/errors2.txt +++ b/pkg/compiler/testdata/errors2.txt @@ -386,3 +386,15 @@ s405 { f3 int64:16[-65541:-10] ### int range [18446744073709486075:18446744073709551606] is too large for base type of size 16 f4 int16:8[-255:0] ### int range [18446744073709551361:0] is too large for base type of size 8 } + +# Field attributes. + +foo$overlay(a ptr[in, struct$overlay0]) + +struct$overlay0 { + f0 int32 + f1 const[0, int32] (out_overlay) ### const type must not be used as output + f2 ptr[in, int32] ### ptr type must not be used as output + f3 proc[0, 1, int32] ### proc type must not be used as output + f4 bytesize[f1, int32] ### bytesize type must not be used as output +} diff --git a/pkg/compiler/types.go b/pkg/compiler/types.go index 53ba8c86f..8454a92d5 100644 --- a/pkg/compiler/types.go +++ b/pkg/compiler/types.go @@ -19,6 +19,7 @@ type typeDesc struct { Names []string CanBeTypedef bool // can be type alias target? CantBeOpt bool // can't be marked as opt? + CantBeOut bool // can't be used as an explicitly output argument NeedBase bool // needs base type when used as field? MaxColon int // max number of colons (int8:2) on fields OptArgs int // number of optional arguments in Args array @@ -164,6 +165,7 @@ func getIntAlignment(comp *compiler, base prog.IntTypeCommon) uint64 { var typePtr = &typeDesc{ Names: []string{"ptr", "ptr64"}, CanBeArgRet: canBeArg, + CantBeOut: true, CanBeTypedef: true, Args: []namedArg{{Name: "direction", Type: typeArgDir}, {Name: "type", Type: typeArgType}}, Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { @@ -289,6 +291,7 @@ var typeLen = &typeDesc{ Names: []string{"len", "bytesize", "bytesize2", "bytesize4", "bytesize8", "bitsize", "offsetof"}, CanBeArgRet: canBeArg, CantBeOpt: true, + CantBeOut: true, NeedBase: true, Args: []namedArg{{Name: "len target", Type: typeArgLenTarget}}, Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { @@ -325,6 +328,7 @@ var typeConst = &typeDesc{ CanBeArgRet: canBeArg, CanBeTypedef: true, CantBeOpt: true, + CantBeOut: true, NeedBase: true, Args: []namedArg{{Name: "value", Type: typeArgInt}}, CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { @@ -367,6 +371,7 @@ var typeFlags = &typeDesc{ CanBeArgRet: canBeArg, CanBeTypedef: true, CantBeOpt: true, + CantBeOut: true, NeedBase: true, Args: []namedArg{{Name: "flags", Type: typeArgFlags}}, CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { @@ -469,6 +474,7 @@ var typeCsum = &typeDesc{ Names: []string{"csum"}, NeedBase: true, CantBeOpt: true, + CantBeOut: true, OptArgs: 1, Args: []namedArg{ {Name: "csum target", Type: typeArgLenTarget}, @@ -517,6 +523,7 @@ func genCsumKind(t *ast.Type) prog.CsumKind { var typeProc = &typeDesc{ Names: []string{"proc"}, CanBeArgRet: canBeArg, + CantBeOut: true, CanBeTypedef: true, NeedBase: true, Args: []namedArg{ @@ -555,6 +562,7 @@ var typeProc = &typeDesc{ var typeText = &typeDesc{ Names: []string{"text"}, CantBeOpt: true, + CantBeOut: true, Args: []namedArg{{Name: "kind", Type: typeArgTextType}}, Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type) bool { return true @@ -754,6 +762,7 @@ var typeFmt = &typeDesc{ Names: []string{"fmt"}, CanBeTypedef: true, CantBeOpt: true, + CantBeOut: true, Args: []namedArg{ {Name: "format", Type: typeFmtFormat}, {Name: "value", Type: typeArgType, IsArg: true}, @@ -908,7 +917,7 @@ func init() { } // Need to cache type in structTypes before generating fields to break recursion. comp.structTypes[t.Ident] = typ - fields := comp.genFieldArray(s.Fields, make([]uint64, len(s.Fields))) + fields, overlayField := comp.genFieldArray(s.Fields, make([]uint64, len(s.Fields))) switch typ1 := typ.(type) { case *prog.UnionType: typ1.Fields = fields @@ -919,6 +928,9 @@ func init() { } case *prog.StructType: typ1.Fields = fields + if overlayField >= 0 { + typ1.OverlayField = overlayField + } attrs := comp.parseAttrs(structAttrs, s, s.Attrs) if align := attrs[attrAlign]; align != 0 { typ1.TypeAlign = align -- cgit mrf-deployment