github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/compiler/gen.go (about)

     1  // Copyright 2017 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  package compiler
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"reflect"
    10  	"sort"
    11  
    12  	"github.com/google/syzkaller/pkg/ast"
    13  	"github.com/google/syzkaller/pkg/serializer"
    14  	"github.com/google/syzkaller/prog"
    15  )
    16  
    17  const sizeUnassigned = ^uint64(0)
    18  
    19  func (comp *compiler) genResources() []*prog.ResourceDesc {
    20  	var resources []*prog.ResourceDesc
    21  	for name, n := range comp.resources {
    22  		if !comp.used[name] {
    23  			continue
    24  		}
    25  		resources = append(resources, comp.genResource(n))
    26  	}
    27  	sort.Slice(resources, func(i, j int) bool {
    28  		return resources[i].Name < resources[j].Name
    29  	})
    30  	return resources
    31  }
    32  
    33  func (comp *compiler) genResource(n *ast.Resource) *prog.ResourceDesc {
    34  	res := &prog.ResourceDesc{
    35  		Name: n.Name.Name,
    36  	}
    37  	for n != nil {
    38  		res.Values = append(genIntArray(n.Values), res.Values...)
    39  		res.Kind = append([]string{n.Name.Name}, res.Kind...)
    40  		n = comp.resources[n.Base.Ident]
    41  	}
    42  	if len(res.Values) == 0 {
    43  		res.Values = []uint64{0}
    44  	}
    45  	return res
    46  }
    47  
    48  func (comp *compiler) collectCallArgSizes() map[string][]uint64 {
    49  	argPos := make(map[string]ast.Pos)
    50  	callArgSizes := make(map[string][]uint64)
    51  	for _, decl := range comp.desc.Nodes {
    52  		n, ok := decl.(*ast.Call)
    53  		if !ok {
    54  			continue
    55  		}
    56  		// Figure out number of arguments and their sizes for each syscall.
    57  		// For example, we may have:
    58  		// ioctl(fd fd, cmd int32, arg intptr)
    59  		// ioctl$FOO(fd fd, cmd const[FOO])
    60  		// Here we will figure out that ioctl$FOO have 3 args, even that
    61  		// only 2 are specified and that size of cmd is 4 even that
    62  		// normally we would assume it's 8 (intptr).
    63  		argSizes := callArgSizes[n.CallName]
    64  		for i, arg := range n.Args {
    65  			if len(argSizes) <= i {
    66  				argSizes = append(argSizes, comp.ptrSize)
    67  			}
    68  			desc, _, _ := comp.getArgsBase(arg.Type, true)
    69  			typ := comp.genField(arg, comp.ptrSize, prog.DirInOut)
    70  			// Ignore all types with base (const, flags). We don't have base in syscall args.
    71  			// Also ignore resources and pointers because fd can be 32-bits and pointer 64-bits,
    72  			// and then there is no way to fix this.
    73  			// The only relevant types left is plain int types.
    74  			if desc != typeInt {
    75  				continue
    76  			}
    77  			if !comp.target.Int64SyscallArgs && typ.Size() > comp.ptrSize {
    78  				comp.error(arg.Pos, "%v arg %v is larger than pointer size", n.Name.Name, arg.Name.Name)
    79  				continue
    80  			}
    81  			argID := fmt.Sprintf("%v|%v", n.CallName, i)
    82  			if _, ok := argPos[argID]; !ok {
    83  				argSizes[i] = typ.Size()
    84  				argPos[argID] = arg.Pos
    85  				continue
    86  			}
    87  			if argSizes[i] != typ.Size() {
    88  				comp.error(arg.Pos, "%v arg %v is redeclared with size %v, previously declared with size %v at %v",
    89  					n.Name.Name, arg.Name.Name, typ.Size(), argSizes[i], argPos[argID])
    90  				continue
    91  			}
    92  		}
    93  		callArgSizes[n.CallName] = argSizes
    94  	}
    95  	return callArgSizes
    96  }
    97  
    98  func (comp *compiler) genSyscalls() []*prog.Syscall {
    99  	callArgSizes := comp.collectCallArgSizes()
   100  	var calls []*prog.Syscall
   101  	for _, decl := range comp.desc.Nodes {
   102  		if n, ok := decl.(*ast.Call); ok && n.NR != ^uint64(0) {
   103  			calls = append(calls, comp.genSyscall(n, callArgSizes[n.CallName]))
   104  		}
   105  	}
   106  	// We assign SquashableElem here rather than during pointer type generation
   107  	// because during pointer generation recursive struct types may not be fully
   108  	// generated yet, thus ForeachArgType won't observe all types.
   109  	prog.ForeachTypePost(calls, func(typ prog.Type, ctx *prog.TypeCtx) {
   110  		if ptr, ok := typ.(*prog.PtrType); ok {
   111  			ptr.SquashableElem = isSquashableElem(ptr.Elem, ptr.ElemDir)
   112  		}
   113  	})
   114  	sort.Slice(calls, func(i, j int) bool {
   115  		return calls[i].Name < calls[j].Name
   116  	})
   117  	return calls
   118  }
   119  
   120  func (comp *compiler) genSyscall(n *ast.Call, argSizes []uint64) *prog.Syscall {
   121  	var ret prog.Type
   122  	if n.Ret != nil {
   123  		ret = comp.genType(n.Ret, comp.ptrSize)
   124  	}
   125  	var attrs prog.SyscallAttrs
   126  	descAttrs := comp.parseIntAttrs(callAttrs, n, n.Attrs)
   127  	for desc, val := range descAttrs {
   128  		fld := reflect.ValueOf(&attrs).Elem().FieldByName(desc.Name)
   129  		switch desc.Type {
   130  		case intAttr:
   131  			fld.SetUint(val)
   132  		case flagAttr:
   133  			fld.SetBool(val != 0)
   134  		default:
   135  			panic(fmt.Sprintf("unexpected attrDesc type: %q", desc.Type))
   136  		}
   137  	}
   138  	fields, _ := comp.genFieldArray(n.Args, argSizes)
   139  	return &prog.Syscall{
   140  		Name:        n.Name.Name,
   141  		CallName:    n.CallName,
   142  		NR:          n.NR,
   143  		MissingArgs: len(argSizes) - len(n.Args),
   144  		Args:        fields,
   145  		Ret:         ret,
   146  		Attrs:       attrs,
   147  	}
   148  }
   149  
   150  type typeProxy struct {
   151  	typ       prog.Type
   152  	id        string
   153  	ref       prog.Ref
   154  	locations []*prog.Type
   155  }
   156  
   157  func (comp *compiler) generateTypes(syscalls []*prog.Syscall) []prog.Type {
   158  	// Replace all Type's in the descriptions with Ref's
   159  	// and prepare a sorted array of corresponding real types.
   160  	proxies := make(map[string]*typeProxy)
   161  	prog.ForeachTypePost(syscalls, func(typ prog.Type, ctx *prog.TypeCtx) {
   162  		if _, ok := typ.(prog.Ref); ok {
   163  			return
   164  		}
   165  		if !typ.Varlen() && typ.Size() == sizeUnassigned {
   166  			panic("unassigned size")
   167  		}
   168  		id := typ.Name()
   169  		switch typ.(type) {
   170  		case *prog.StructType, *prog.UnionType:
   171  			// There types can be uniquely identified with the name.
   172  		default:
   173  			buf := new(bytes.Buffer)
   174  			serializer.Write(buf, typ)
   175  			id = buf.String()
   176  		}
   177  		proxy := proxies[id]
   178  		if proxy == nil {
   179  			proxy = &typeProxy{
   180  				typ: typ,
   181  				id:  id,
   182  				ref: prog.Ref(len(proxies)),
   183  			}
   184  			proxies[id] = proxy
   185  		}
   186  		*ctx.Ptr = proxy.ref
   187  		proxy.locations = append(proxy.locations, ctx.Ptr)
   188  	})
   189  	array := make([]*typeProxy, 0, len(proxies))
   190  	for _, proxy := range proxies {
   191  		array = append(array, proxy)
   192  	}
   193  	sort.Slice(array, func(i, j int) bool {
   194  		return array[i].id < array[j].id
   195  	})
   196  	types := make([]prog.Type, len(array))
   197  	for i, proxy := range array {
   198  		types[i] = proxy.typ
   199  		for _, loc := range proxy.locations {
   200  			*loc = prog.Ref(i)
   201  		}
   202  	}
   203  	return types
   204  }
   205  
   206  func (comp *compiler) layoutTypes(syscalls []*prog.Syscall) {
   207  	// Calculate struct/union/array sizes, add padding to structs, mark bitfields.
   208  	padded := make(map[prog.Type]bool)
   209  	prog.ForeachTypePost(syscalls, func(typ prog.Type, _ *prog.TypeCtx) {
   210  		comp.layoutType(typ, padded)
   211  	})
   212  }
   213  
   214  func (comp *compiler) layoutType(typ prog.Type, padded map[prog.Type]bool) {
   215  	if padded[typ] {
   216  		return
   217  	}
   218  	switch t := typ.(type) {
   219  	case *prog.ArrayType:
   220  		comp.layoutType(t.Elem, padded)
   221  		comp.layoutArray(t)
   222  	case *prog.StructType:
   223  		for _, f := range t.Fields {
   224  			comp.layoutType(f.Type, padded)
   225  		}
   226  		comp.layoutStruct(t)
   227  	case *prog.UnionType:
   228  		for _, f := range t.Fields {
   229  			comp.layoutType(f.Type, padded)
   230  		}
   231  		comp.layoutUnion(t)
   232  	default:
   233  		return
   234  	}
   235  	if !typ.Varlen() && typ.Size() == sizeUnassigned {
   236  		panic("size unassigned")
   237  	}
   238  	padded[typ] = true
   239  }
   240  
   241  func (comp *compiler) layoutArray(t *prog.ArrayType) {
   242  	t.TypeSize = 0
   243  	if t.Kind == prog.ArrayRangeLen && t.RangeBegin == t.RangeEnd && !t.Elem.Varlen() {
   244  		t.TypeSize = t.RangeBegin * t.Elem.Size()
   245  	}
   246  }
   247  
   248  func (comp *compiler) layoutUnion(t *prog.UnionType) {
   249  	t.TypeSize = 0
   250  	structNode := comp.structs[t.TypeName]
   251  	if structNode == conditionalFieldWrapper {
   252  		return
   253  	}
   254  	attrs := comp.parseIntAttrs(unionAttrs, structNode, structNode.Attrs)
   255  	if attrs[attrVarlen] != 0 {
   256  		return
   257  	}
   258  	sizeAttr, hasSize := attrs[attrSize]
   259  	for i, fld := range t.Fields {
   260  		sz := fld.Size()
   261  		if hasSize && sz > sizeAttr {
   262  			comp.error(structNode.Fields[i].Pos, "union %v has size attribute %v"+
   263  				" which is less than field %v size %v",
   264  				structNode.Name.Name, sizeAttr, fld.Type.Name(), sz)
   265  		}
   266  		if t.TypeSize < sz {
   267  			t.TypeSize = sz
   268  		}
   269  	}
   270  	if hasSize {
   271  		t.TypeSize = sizeAttr
   272  	}
   273  }
   274  
   275  func (comp *compiler) layoutStruct(t *prog.StructType) {
   276  	// Add paddings, calculate size, mark bitfields.
   277  	structNode := comp.structs[t.TypeName]
   278  	varlen := false
   279  	for _, f := range t.Fields {
   280  		if f.Varlen() {
   281  			varlen = true
   282  		}
   283  	}
   284  	attrs := comp.parseIntAttrs(structAttrs, structNode, structNode.Attrs)
   285  	t.AlignAttr = attrs[attrAlign]
   286  	comp.layoutStructFields(t, varlen, attrs[attrPacked] != 0)
   287  	t.TypeSize = 0
   288  	if !varlen {
   289  		var size uint64
   290  		for i, f := range t.Fields {
   291  			if i == t.OverlayField {
   292  				size = 0
   293  			}
   294  			size += f.Size()
   295  			if t.TypeSize < size {
   296  				t.TypeSize = size
   297  			}
   298  		}
   299  		sizeAttr, hasSize := attrs[attrSize]
   300  		if hasSize {
   301  			if t.TypeSize > sizeAttr {
   302  				comp.error(structNode.Attrs[0].Pos, "struct %v has size attribute %v"+
   303  					" which is less than struct size %v",
   304  					structNode.Name.Name, sizeAttr, t.TypeSize)
   305  			}
   306  			if pad := sizeAttr - t.TypeSize; pad != 0 {
   307  				t.Fields = append(t.Fields, genPad(pad))
   308  			}
   309  			t.TypeSize = sizeAttr
   310  		}
   311  	}
   312  }
   313  
   314  func (comp *compiler) layoutStructFields(t *prog.StructType, varlen, packed bool) {
   315  	var newFields []prog.Field
   316  	overlayField0 := t.OverlayField
   317  	var structAlign, byteOffset, bitOffset uint64
   318  	for i, field := range t.Fields {
   319  		f := field.Type
   320  		if i == overlayField0 {
   321  			// We layout fields before overlay and the overlay fields effectively as 2 independent structs.
   322  			// So if we starting overlay, add any trailign padding/finalize bitfield layout and reset state.
   323  			newFields = comp.finalizeStructFields(t, newFields, varlen, structAlign, byteOffset, bitOffset)
   324  			t.OverlayField = len(newFields) // update overlay field index after we added paddings
   325  			structAlign, byteOffset, bitOffset = 0, 0, 0
   326  		}
   327  		fieldAlign := uint64(1)
   328  		if !packed {
   329  			fieldAlign = f.Alignment()
   330  			if structAlign < fieldAlign {
   331  				structAlign = fieldAlign
   332  			}
   333  		}
   334  		fullBitOffset := byteOffset*8 + bitOffset
   335  		var fieldOffset uint64
   336  
   337  		if f.IsBitfield() {
   338  			unitAlign := f.UnitSize()
   339  			if packed {
   340  				unitAlign = 1
   341  			}
   342  			fieldOffset = rounddown(fullBitOffset/8, unitAlign)
   343  			unitBits := f.UnitSize() * 8
   344  			occupiedBits := fullBitOffset - fieldOffset*8
   345  			remainBits := unitBits - occupiedBits
   346  
   347  			if remainBits < f.BitfieldLength() {
   348  				fieldOffset = roundup(roundup(fullBitOffset, 8)/8, unitAlign)
   349  				fullBitOffset, bitOffset = 0, 0
   350  			} else if fieldOffset*8 >= fullBitOffset {
   351  				fullBitOffset, bitOffset = fieldOffset*8, 0
   352  			}
   353  			fieldBitOffset := (fullBitOffset - fieldOffset*8) % unitBits
   354  			setBitfieldOffset(f, fieldBitOffset)
   355  		} else {
   356  			fieldOffset = roundup(roundup(fullBitOffset, 8)/8, fieldAlign)
   357  			bitOffset = 0
   358  		}
   359  		if fieldOffset > byteOffset {
   360  			pad := fieldOffset - byteOffset
   361  			byteOffset += pad
   362  			if i != 0 && t.Fields[i-1].IsBitfield() {
   363  				setBitfieldTypeSize(t.Fields[i-1].Type, pad)
   364  				if bitOffset >= 8*pad {
   365  					// The padding is due to bitfields, so consume the bitOffset.
   366  					bitOffset -= 8 * pad
   367  				} else if bitOffset >= 8 {
   368  					// Unclear is this is a bug or not and what to do in this case.
   369  					// But since we don't have any descriptions that trigger this,
   370  					// let's just guard with the panic.
   371  					panic(fmt.Sprintf("bad bitOffset: %v.%v pad=%v bitOffset=%v",
   372  						t.Name(), field.Name, pad, bitOffset))
   373  				}
   374  			} else {
   375  				newFields = append(newFields, genPad(pad))
   376  			}
   377  		}
   378  		if f.IsBitfield() {
   379  			if byteOffset > fieldOffset {
   380  				unitOffset := byteOffset - fieldOffset
   381  				setBitfieldUnitOffset(f, unitOffset)
   382  			}
   383  		}
   384  		newFields = append(newFields, field)
   385  		if f.IsBitfield() {
   386  			bitOffset += f.BitfieldLength()
   387  		} else if !f.Varlen() {
   388  			// Increase offset if the current field except when it's
   389  			// the last field in a struct and has variable length.
   390  			byteOffset += f.Size()
   391  		}
   392  	}
   393  	t.Fields = comp.finalizeStructFields(t, newFields, varlen, structAlign, byteOffset, bitOffset)
   394  }
   395  
   396  func (comp *compiler) finalizeStructFields(t *prog.StructType, fields []prog.Field, varlen bool,
   397  	structAlign, byteOffset, bitOffset uint64) []prog.Field {
   398  	if bitOffset != 0 {
   399  		pad := roundup(bitOffset, 8) / 8
   400  		byteOffset += pad
   401  		i := len(fields)
   402  		if i != 0 && fields[i-1].IsBitfield() {
   403  			setBitfieldTypeSize(fields[i-1].Type, pad)
   404  		} else {
   405  			fields = append(fields, genPad(pad))
   406  		}
   407  	}
   408  
   409  	if t.AlignAttr != 0 {
   410  		structAlign = t.AlignAttr
   411  	}
   412  	if !varlen && structAlign != 0 && byteOffset%structAlign != 0 {
   413  		pad := structAlign - byteOffset%structAlign
   414  		fields = append(fields, genPad(pad))
   415  	}
   416  	return fields
   417  }
   418  
   419  func roundup(v, a uint64) uint64 {
   420  	return rounddown(v+a-1, a)
   421  }
   422  
   423  func rounddown(v, a uint64) uint64 {
   424  	if (a & (a - 1)) != 0 {
   425  		panic(fmt.Sprintf("rounddown(%v)", a))
   426  	}
   427  	return v & ^(a - 1)
   428  }
   429  
   430  func bitfieldFields(t0 prog.Type) (*uint64, *uint64, *uint64) {
   431  	switch t := t0.(type) {
   432  	case *prog.IntType:
   433  		return &t.TypeSize, &t.BitfieldOff, &t.BitfieldUnitOff
   434  	case *prog.ConstType:
   435  		return &t.TypeSize, &t.BitfieldOff, &t.BitfieldUnitOff
   436  	case *prog.LenType:
   437  		return &t.TypeSize, &t.BitfieldOff, &t.BitfieldUnitOff
   438  	case *prog.FlagsType:
   439  		return &t.TypeSize, &t.BitfieldOff, &t.BitfieldUnitOff
   440  	case *prog.ProcType:
   441  		return &t.TypeSize, &t.BitfieldOff, &t.BitfieldUnitOff
   442  	default:
   443  		panic(fmt.Sprintf("type %#v can't be a bitfield", t))
   444  	}
   445  }
   446  
   447  func setBitfieldTypeSize(t prog.Type, v uint64) {
   448  	p, _, _ := bitfieldFields(t)
   449  	*p = v
   450  }
   451  
   452  func setBitfieldOffset(t prog.Type, v uint64) {
   453  	_, p, _ := bitfieldFields(t)
   454  	*p = v
   455  }
   456  
   457  func setBitfieldUnitOffset(t prog.Type, v uint64) {
   458  	_, _, p := bitfieldFields(t)
   459  	*p = v
   460  }
   461  
   462  func genPad(size uint64) prog.Field {
   463  	return prog.Field{
   464  		Type: &prog.ConstType{
   465  			IntTypeCommon: genIntCommon(genCommon("pad", size, false), 0, false),
   466  			IsPad:         true,
   467  		},
   468  	}
   469  }
   470  
   471  func (comp *compiler) genFieldArray(fields []*ast.Field, argSizes []uint64) ([]prog.Field, int) {
   472  	outOverlay := -1
   473  	for i, f := range fields {
   474  		intAttrs := comp.parseIntAttrs(structFieldAttrs, f, f.Attrs)
   475  		if intAttrs[attrOutOverlay] > 0 {
   476  			outOverlay = i
   477  		}
   478  	}
   479  	var res []prog.Field
   480  	for i, f := range fields {
   481  		overlayDir := prog.DirInOut
   482  		if outOverlay != -1 {
   483  			overlayDir = prog.DirIn
   484  			if i >= outOverlay {
   485  				overlayDir = prog.DirOut
   486  			}
   487  		}
   488  		res = append(res, comp.genField(f, argSizes[i], overlayDir))
   489  	}
   490  	return res, outOverlay
   491  }
   492  
   493  func (comp *compiler) genFieldDir(attrs map[*attrDesc]uint64) (prog.Dir, bool) {
   494  	switch {
   495  	case attrs[attrIn] != 0:
   496  		return prog.DirIn, true
   497  	case attrs[attrOut] != 0:
   498  		return prog.DirOut, true
   499  	case attrs[attrInOut] != 0:
   500  		return prog.DirInOut, true
   501  	default:
   502  		return prog.DirIn, false
   503  	}
   504  }
   505  
   506  func (comp *compiler) genField(f *ast.Field, argSize uint64, overlayDir prog.Dir) prog.Field {
   507  	intAttrs, exprAttrs := comp.parseAttrs(structFieldAttrs, f, f.Attrs)
   508  	dir, hasDir := overlayDir, true
   509  	if overlayDir == prog.DirInOut {
   510  		dir, hasDir = comp.genFieldDir(intAttrs)
   511  	}
   512  	return prog.Field{
   513  		Name:         f.Name.Name,
   514  		Type:         comp.genType(f.Type, argSize),
   515  		HasDirection: hasDir,
   516  		Direction:    dir,
   517  		Condition:    exprAttrs[attrIf],
   518  	}
   519  }
   520  
   521  var conditionalFieldWrapper = &ast.Struct{}
   522  
   523  // For structs, we wrap conditional fields in anonymous unions with a @void field.
   524  func (comp *compiler) wrapConditionalField(name string, field prog.Field) prog.Field {
   525  	common := genCommon(fmt.Sprintf("_%s_%s_wrapper", name, field.Name), sizeUnassigned, false)
   526  	common.IsVarlen = true
   527  	common.TypeAlign = field.Type.Alignment()
   528  
   529  	// Fake the corresponding ast.Struct.
   530  	comp.structs[common.TypeName] = conditionalFieldWrapper
   531  
   532  	voidBase := genIntCommon(genCommon("void", sizeUnassigned, false), 0, false)
   533  	voidType := typeVoid.Gen(comp, nil, nil, voidBase)
   534  
   535  	var newCondition prog.Expression
   536  	if field.Condition != nil {
   537  		newCondition = field.Condition.Clone()
   538  		// Prepend "parent:".
   539  		newCondition.ForEachValue(func(val *prog.Value) {
   540  			if len(val.Path) == 0 {
   541  				return
   542  			}
   543  			if val.Path[0] == prog.ParentRef {
   544  				val.Path = append([]string{prog.ParentRef}, val.Path...)
   545  			} else {
   546  				// Single "parent:" would not change anything.
   547  				val.Path = append([]string{prog.ParentRef, prog.ParentRef}, val.Path...)
   548  			}
   549  		})
   550  	}
   551  
   552  	return prog.Field{
   553  		Name: field.Name,
   554  		Type: &prog.UnionType{
   555  			TypeCommon: common,
   556  			Fields: []prog.Field{
   557  				{
   558  					Name:         "value",
   559  					Type:         field.Type,
   560  					HasDirection: field.HasDirection,
   561  					Direction:    field.Direction,
   562  					Condition:    newCondition,
   563  				},
   564  				{
   565  					Name: "void",
   566  					Type: voidType,
   567  					Condition: &prog.BinaryExpression{
   568  						Operator: prog.OperatorCompareEq,
   569  						Left:     newCondition,
   570  						Right:    &prog.Value{Value: 0x0, Path: nil},
   571  					},
   572  				},
   573  			},
   574  		},
   575  	}
   576  }
   577  
   578  func (comp *compiler) genType(t *ast.Type, argSize uint64) prog.Type {
   579  	desc, args, base := comp.getArgsBase(t, argSize != 0)
   580  	if desc.Gen == nil {
   581  		panic(fmt.Sprintf("no gen for %v %#v", t.Ident, t))
   582  	}
   583  	if argSize != 0 {
   584  		// Now that we know a more precise size, patch the type.
   585  		// This is somewhat hacky. Ideally we figure out the size earlier,
   586  		// store it somewhere and use during generation of the arg base type.
   587  		base.TypeSize = argSize
   588  		if desc.CheckConsts != nil {
   589  			desc.CheckConsts(comp, t, args, base)
   590  		}
   591  	}
   592  	base.IsVarlen = desc.Varlen != nil && desc.Varlen(comp, t, args)
   593  	return desc.Gen(comp, t, args, base)
   594  }
   595  
   596  const valueIdent = "value"
   597  
   598  var binaryOperatorMap = map[ast.Operator]prog.BinaryOperator{
   599  	ast.OperatorCompareEq:  prog.OperatorCompareEq,
   600  	ast.OperatorCompareNeq: prog.OperatorCompareNeq,
   601  	ast.OperatorBinaryAnd:  prog.OperatorBinaryAnd,
   602  }
   603  
   604  func (comp *compiler) genExpression(t *ast.Type) prog.Expression {
   605  	if binary := t.Expression; binary != nil {
   606  		operator, ok := binaryOperatorMap[binary.Operator]
   607  		if !ok {
   608  			comp.error(binary.Pos, "unknown binary operator")
   609  			return nil
   610  		}
   611  		return &prog.BinaryExpression{
   612  			Operator: operator,
   613  			Left:     comp.genExpression(binary.Left),
   614  			Right:    comp.genExpression(binary.Right),
   615  		}
   616  	} else {
   617  		return comp.genValue(t)
   618  	}
   619  }
   620  
   621  func (comp *compiler) genValue(val *ast.Type) *prog.Value {
   622  	if val.Ident == valueIdent {
   623  		if len(val.Args) != 1 {
   624  			comp.error(val.Pos, "value reference must have only one argument")
   625  			return nil
   626  		}
   627  		arg := val.Args[0]
   628  		if arg.Args != nil {
   629  			comp.error(val.Pos, "value aguments must not have any further arguments")
   630  			return nil
   631  		}
   632  		path := []string{arg.Ident}
   633  		for _, elem := range arg.Colon {
   634  			if elem.Args != nil {
   635  				comp.error(arg.Pos, "value path elements must not have any attributes")
   636  				return nil
   637  			}
   638  			path = append(path, elem.Ident)
   639  		}
   640  		return &prog.Value{Path: path}
   641  	}
   642  	if val.Expression != nil || val.HasString {
   643  		comp.error(val.Pos, "the token must be either an integer or an identifier")
   644  		return nil
   645  	}
   646  	if len(val.Args) != 0 {
   647  		comp.error(val.Pos, "consts in expressions must not have any arguments")
   648  		return nil
   649  	}
   650  	return &prog.Value{
   651  		Value: val.Value,
   652  	}
   653  }
   654  
   655  func genCommon(name string, size uint64, opt bool) prog.TypeCommon {
   656  	return prog.TypeCommon{
   657  		TypeName:   name,
   658  		TypeSize:   size,
   659  		IsOptional: opt,
   660  	}
   661  }
   662  
   663  func genIntCommon(com prog.TypeCommon, bitLen uint64, bigEndian bool) prog.IntTypeCommon {
   664  	bf := prog.FormatNative
   665  	if bigEndian {
   666  		bf = prog.FormatBigEndian
   667  	}
   668  	bfUnit := uint64(0)
   669  	if bitLen != 0 {
   670  		bfUnit = com.TypeSize
   671  		com.TypeSize = 0
   672  	}
   673  	return prog.IntTypeCommon{
   674  		TypeCommon:   com,
   675  		ArgFormat:    bf,
   676  		BitfieldLen:  bitLen,
   677  		BitfieldUnit: bfUnit,
   678  	}
   679  }
   680  
   681  func genIntArray(a []*ast.Int) []uint64 {
   682  	r := make([]uint64, len(a))
   683  	for i, v := range a {
   684  		r[i] = v.Value
   685  	}
   686  	return r
   687  }
   688  
   689  func genStrArray(a []*ast.String) []string {
   690  	r := make([]string, len(a))
   691  	for i, v := range a {
   692  		r[i] = v.Value
   693  	}
   694  	return r
   695  }