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