aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2022-01-05 20:05:11 +0100
committerDmitry Vyukov <dvyukov@google.com>2022-01-11 16:30:08 +0100
commit2cfe62f82077ba012aef55db5288985bc0c426d9 (patch)
tree501d83deb358a230d157c9d7efc6fc393e239472 /pkg
parent16e21d13ea26a631e9b3a30c94635b1d565fd78f (diff)
pkg/compiler: add out_overlay field attribute
Diffstat (limited to 'pkg')
-rw-r--r--pkg/compiler/attrs.go17
-rw-r--r--pkg/compiler/check.go36
-rw-r--r--pkg/compiler/gen.go57
-rw-r--r--pkg/compiler/testdata/all.txt9
-rw-r--r--pkg/compiler/testdata/errors.txt21
-rw-r--r--pkg/compiler/testdata/errors2.txt12
-rw-r--r--pkg/compiler/types.go14
7 files changed, 141 insertions, 25 deletions
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