prog, pkg/compiler: alignment for integer ranges

Enables the syntax intN[start:end, alignment] for integer ranges.  For
instance, int32[0:10, 2] represents even 32-bit numbers between 0 and 10
included.  With this change, two NEED tags in syscall descriptions can be
addressed.

Signed-off-by: Paul Chaignon <paul.chaignon@orange.com>
diff --git a/docs/syscall_descriptions_syntax.md b/docs/syscall_descriptions_syntax.md
index 9fb764b..28dcc5d 100644
--- a/docs/syscall_descriptions_syntax.md
+++ b/docs/syscall_descriptions_syntax.md
@@ -25,7 +25,8 @@
 "const": integer constant, type-options:
 	value, underlying type (one of "intN", "intptr")
 "intN"/"intptr": an integer without a particular meaning, type-options:
-	optional range of values (e.g. "5:10", or "100:200")
+	optional range of values (e.g. "5:10", or "100:200"),
+	optionally followed by an alignment parameter
 "flags": a set of flags, type-options:
 	reference to flags description (see below), underlying int type (e.g. "int32")
 "array": a variable/fixed-length array, type-options:
@@ -84,7 +85,7 @@
 
 By appending `be` suffix (e.g. `int16be`) integers become big-endian.
 
-It's possible to specify range of values for an integer in the format of `int32[0:100]`.
+It's possible to specify a range of values for an integer in the format of `int32[0:100]` or `int32[0:4096, 512]` for a 512-aligned int.
 
 To denote a bitfield of size N use `int64:N`.
 
@@ -95,7 +96,8 @@
 	f0	int8			# random 1-byte integer
 	f1	const[0x42, int16be]	# const 2-byte integer with value 0x4200 (big-endian 0x42)
 	f2	int32[0:100]		# random 4-byte integer with values from 0 to 100 inclusive
-	f3	int64:20		# random 20-bit bitfield
+	f3	int32[1:10, 2]		# random 4-byte integer with values {1, 3, 5, 7, 9}
+	f4	int64:20		# random 20-bit bitfield
 }
 ```
 
diff --git a/pkg/compiler/consts_test.go b/pkg/compiler/consts_test.go
index 37c2625..69810c8 100644
--- a/pkg/compiler/consts_test.go
+++ b/pkg/compiler/consts_test.go
@@ -37,7 +37,8 @@
 		"CONST6", "CONST7", "CONST8", "CONST9", "CONST10",
 		"CONST11", "CONST12", "CONST13", "CONST14", "CONST15",
 		"CONST16", "CONST17", "CONST18", "CONST19", "CONST20",
-		"CONST21", "CONST22", "CONST23", "CONST24",
+		"CONST21", "CONST22", "CONST23", "CONST24", "CONST25",
+		"CONST26",
 	}
 	sort.Strings(wantConsts)
 	if !reflect.DeepEqual(info.Consts, wantConsts) {
diff --git a/pkg/compiler/testdata/all.txt b/pkg/compiler/testdata/all.txt
index 9bb2c3d..82aad66 100644
--- a/pkg/compiler/testdata/all.txt
+++ b/pkg/compiler/testdata/all.txt
@@ -12,6 +12,8 @@
 foo$8(a ptr[in, strings])
 foo$9(a ptr[out, ptr[in, string]])
 foo$10(a ptr[out, buffer[in]])
+foo$11(a int64[1:100, 2])
+foo$12(a int64[0:-1, 0x1000])
 
 resource r0[intptr]
 
@@ -166,6 +168,9 @@
 bitfield0 {
 	f1	int8:1
 	f2	int8:2
+	f3	int16:8[-255:0]
+	f4	int16:8[0:255]
+	f5	int64:64[-1:1]
 }
 
 foo$bitfield0(a ptr[in, bitfield0])
diff --git a/pkg/compiler/testdata/consts.txt b/pkg/compiler/testdata/consts.txt
index ef248a1..29cdfb6 100644
--- a/pkg/compiler/testdata/consts.txt
+++ b/pkg/compiler/testdata/consts.txt
@@ -21,7 +21,7 @@
 
 bar$BAZ(x vma[opt], y vma[CONST8], z vma[CONST9:CONST10])
 bar$QUX(s ptr[in, string["foo", CONST11]], x ptr[in, csum[s, pseudo, CONST12, int16]])
-bar$FOO(x int8[8:CONST13], y int16be[CONST14:10], z intptr[CONST15:CONST16])
+bar$FOO(x int8[8:CONST13], y int16be[CONST14:10], z intptr[CONST15:CONST16], w int32[0:CONST25, CONST26])
 
 type type0 const[CONST17, int8]
 type templ0[C] const[C, int8]
diff --git a/pkg/compiler/testdata/errors.txt b/pkg/compiler/testdata/errors.txt
index a43cc55..d7d8015 100644
--- a/pkg/compiler/testdata/errors.txt
+++ b/pkg/compiler/testdata/errors.txt
@@ -122,6 +122,7 @@
 foo$65(a int32, b len[1])	### unexpected int 1 for len target argument of len type, expect identifier
 foo$66(a int32, b len[a:1])	### unexpected int 1 after colon, expect identifier
 foo$67(x int32[1:2:3, opt])	### unexpected ':'
+foo$68(a int32[15, 2])		### first argument of int32 needs to be a range
 
 opt {				### struct uses reserved name opt
 	f1	int32
diff --git a/pkg/compiler/testdata/errors2.txt b/pkg/compiler/testdata/errors2.txt
index db8f87e..a2e1d68 100644
--- a/pkg/compiler/testdata/errors2.txt
+++ b/pkg/compiler/testdata/errors2.txt
@@ -254,6 +254,13 @@
 foo$513(a ptr[in, array[int8, -2:2]])	### bad size range [18446744073709551614:2]
 foo$514(a vma[-2:2])			### bad size range [18446744073709551614:2]
 foo$515(a ptr[in, proc[1, -10, int64]])	### values starting from 1 with step 18446744073709551606 overflow base type for 32 procs
+foo$516(a ptr[in, intptr[0:2, 1]])	### bad int alignment 1
+foo$517(a intptr[0:0xffffffff, 0])	### bad int alignment 0
+foo$518(a int64[1:100, -1])		### int alignment 18446744073709551615 is too large for range [1:100]
+foo$519(a int64[0:10, 512])		### int alignment 512 is too large for range [0:10]
+foo$520(a int8[0:16, 0xffff])		### int alignment 65535 is too large for range [0:16]
+foo$521(a int32[9:10, 8])		### int alignment 8 is too large for range [9:10]
+foo$522(a int8[0:-1, 1000])		### int alignment 1000 is too large for range [0:255]
 
 type type500 proc[C1, 8, int8]	### values starting from 1 with step 8 overflow base type for 32 procs
 type type501 int8		### unused type type501
diff --git a/pkg/compiler/types.go b/pkg/compiler/types.go
index 928c15b..32fc366 100644
--- a/pkg/compiler/types.go
+++ b/pkg/compiler/types.go
@@ -68,8 +68,11 @@
 	CanBeArgRet:  canBeArg,
 	CanBeTypedef: true,
 	MaxColon:     1,
-	OptArgs:      1,
-	Args:         []namedArg{{Name: "range", Type: typeArgIntRange}},
+	OptArgs:      2,
+	Args: []namedArg{
+		{Name: "range", Type: typeArgIntRange},
+		{Name: "align", Type: typeArgIntAlign},
+	},
 	CanBeResourceBase: func(comp *compiler, t *ast.Type) bool {
 		// Big-endian resources can always be converted to non-big-endian,
 		// since we will always revert bytes during copyout and during copyin,
@@ -84,15 +87,42 @@
 			comp.error(args[0].Pos, "first argument of %v needs to be a range", t.Ident)
 		}
 	},
+	CheckConsts: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) {
+		if len(args) > 0 && len(args[0].Colon) != 0 {
+			begin := args[0].Value
+			end := args[0].Colon[0].Value
+			size, _ := comp.parseIntType(t.Ident)
+			size = size * 8
+			if len(t.Colon) != 0 {
+				// Integer is bitfield.
+				size = t.Colon[0].Value
+			}
+			if len(args) > 1 && begin == 0 && int64(end) == -1 {
+				// intN[0:-1, align] is a special value for 'all possible values',
+				// but aligned.
+				end = 1<<size - 1
+			} else if end-begin > 1<<64-1<<32 {
+				comp.error(args[0].Pos, "bad int range [%v:%v]", begin, end)
+				return
+			}
+			if len(args) > 1 && args[1].Value != 0 && (end-begin)/args[1].Value == 0 {
+				comp.error(args[1].Pos, "int alignment %v is too large for range [%v:%v]",
+					args[1].Value, begin, end)
+			}
+		}
+	},
 	Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type {
 		size, be := comp.parseIntType(t.Ident)
-		kind, rangeBegin, rangeEnd := prog.IntPlain, uint64(0), uint64(0)
+		kind, rangeBegin, rangeEnd, align := prog.IntPlain, uint64(0), uint64(0), uint64(0)
 		if len(args) > 0 {
 			rangeArg := args[0]
 			kind, rangeBegin, rangeEnd = prog.IntRange, rangeArg.Value, rangeArg.Value
 			if len(rangeArg.Colon) != 0 {
 				rangeEnd = rangeArg.Colon[0].Value
 			}
+			if len(args) > 1 {
+				align = args[1].Value
+			}
 		}
 		var bitLen uint64
 		if len(t.Colon) != 0 {
@@ -104,6 +134,7 @@
 			Kind:          kind,
 			RangeBegin:    rangeBegin,
 			RangeEnd:      rangeEnd,
+			Align:         align,
 		}
 	},
 }
@@ -794,13 +825,14 @@
 var typeArgIntRange = &typeArg{
 	Kind:     kindInt,
 	MaxColon: 1,
+}
+
+var typeArgIntAlign = &typeArg{
+	Kind:     kindInt,
+	MaxColon: 0,
 	CheckConsts: func(comp *compiler, t *ast.Type) {
-		end := t.Value
-		if len(t.Colon) != 0 {
-			end = t.Colon[0].Value
-		}
-		if end-t.Value > 1<<64-1<<32 {
-			comp.error(t.Pos, "bad int range [%v:%v]", t.Value, end)
+		if t.Value <= 1 {
+			comp.error(t.Pos, "bad int alignment %v", t.Value)
 		}
 	},
 }
diff --git a/prog/mutation.go b/prog/mutation.go
index 3f5129f..c9f647a 100644
--- a/prog/mutation.go
+++ b/prog/mutation.go
@@ -254,26 +254,50 @@
 	return
 }
 
-func mutateInt(r *randGen, s *state, arg Arg) (calls []*Call, retry, preserve bool) {
-	if r.bin() {
-		return regenerate(r, s, arg)
-	}
-	bits := arg.Type().TypeBitSize()
-	a := arg.(*ConstArg)
+func mutateInt(r *randGen, a *ConstArg, t *IntType) uint64 {
 	switch {
 	case r.nOutOf(1, 3):
-		a.Val += uint64(r.Intn(4)) + 1
+		return a.Val + (uint64(r.Intn(4)) + 1)
 	case r.nOutOf(1, 2):
-		a.Val -= uint64(r.Intn(4)) + 1
+		return a.Val - (uint64(r.Intn(4)) + 1)
 	default:
-		a.Val ^= 1 << uint64(r.Intn(int(bits)))
+		return a.Val ^ (1 << uint64(r.Intn(int(t.TypeBitSize()))))
 	}
-	a.Val = truncateToBitSize(a.Val, bits)
-	return
+}
+
+func mutateAlignedInt(r *randGen, a *ConstArg, t *IntType) uint64 {
+	rangeEnd := t.RangeEnd
+	if t.RangeBegin == 0 && int64(rangeEnd) == -1 {
+		// Special [0:-1] range for all possible values.
+		rangeEnd = uint64(1<<t.TypeBitSize() - 1)
+	}
+	index := (a.Val - t.RangeBegin) / t.Align
+	misalignment := (a.Val - t.RangeBegin) % t.Align
+	switch {
+	case r.nOutOf(1, 3):
+		index += uint64(r.Intn(4)) + 1
+	case r.nOutOf(1, 2):
+		index -= uint64(r.Intn(4)) + 1
+	default:
+		index ^= 1 << uint64(r.Intn(int(t.TypeBitSize())))
+	}
+	lastIndex := (rangeEnd - t.RangeBegin) / t.Align
+	index %= lastIndex + 1
+	return t.RangeBegin + index*t.Align + misalignment
 }
 
 func (t *IntType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) {
-	return mutateInt(r, s, arg)
+	if r.bin() {
+		return regenerate(r, s, arg)
+	}
+	a := arg.(*ConstArg)
+	if t.Align == 0 {
+		a.Val = mutateInt(r, a, t)
+	} else {
+		a.Val = mutateAlignedInt(r, a, t)
+	}
+	a.Val = truncateToBitSize(a.Val, t.TypeBitSize())
+	return
 }
 
 func (t *FlagsType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) {
@@ -501,7 +525,16 @@
 		return plainPrio, false
 	}
 
-	switch size := t.RangeEnd - t.RangeBegin + 1; {
+	size := t.RangeEnd - t.RangeBegin + 1
+	if t.Align != 0 {
+		if t.RangeBegin == 0 && int64(t.RangeEnd) == -1 {
+			// Special [0:-1] range for all possible values.
+			size = (1<<t.TypeBitSize()-1)/t.Align + 1
+		} else {
+			size = (t.RangeEnd-t.RangeBegin)/t.Align + 1
+		}
+	}
+	switch {
 	case size <= 15:
 		// For a small range, we assume that it is effectively
 		// similar with FlagsType and we need to try all possible values.
diff --git a/prog/rand.go b/prog/rand.go
index 1b02dc7..41738c0 100644
--- a/prog/rand.go
+++ b/prog/rand.go
@@ -119,10 +119,18 @@
 	return v & uint64(1<<bitSize-1)
 }
 
-func (r *randGen) randRangeInt(begin, end, bitSize uint64) uint64 {
+func (r *randGen) randRangeInt(begin, end, bitSize, align uint64) uint64 {
 	if r.oneOf(100) {
 		return r.randInt(bitSize)
 	}
+	if align != 0 {
+		if begin == 0 && int64(end) == -1 {
+			// Special [0:-1] range for all possible values.
+			end = uint64(1<<bitSize - 1)
+		}
+		endAlign := (end - begin) / align
+		return begin + r.randRangeInt(0, endAlign, bitSize, 0)*align
+	}
 	return begin + (r.Uint64() % (end - begin + 1))
 }
 
@@ -753,7 +761,7 @@
 			v = r.randInt(bits)
 		}
 	case IntRange:
-		v = r.randRangeInt(a.RangeBegin, a.RangeEnd, bits)
+		v = r.randRangeInt(a.RangeBegin, a.RangeEnd, bits, a.Align)
 	}
 	return MakeConstArg(a, v), nil
 }
diff --git a/prog/size.go b/prog/size.go
index c465a91..0f3435b 100644
--- a/prog/size.go
+++ b/prog/size.go
@@ -181,9 +181,9 @@
 	if r.bin() {
 		// Small adjustment to trigger missed size checks.
 		if arg.Val != 0 && r.bin() {
-			arg.Val = r.randRangeInt(0, arg.Val-1, arg.Type().TypeBitSize())
+			arg.Val = r.randRangeInt(0, arg.Val-1, arg.Type().TypeBitSize(), 0)
 		} else {
-			arg.Val = r.randRangeInt(arg.Val+1, arg.Val+1000, arg.Type().TypeBitSize())
+			arg.Val = r.randRangeInt(arg.Val+1, arg.Val+1000, arg.Type().TypeBitSize(), 0)
 		}
 		return true
 	}
diff --git a/prog/types.go b/prog/types.go
index 1054090..d2a7382 100644
--- a/prog/types.go
+++ b/prog/types.go
@@ -253,6 +253,7 @@
 	Kind       IntKind
 	RangeBegin uint64
 	RangeEnd   uint64
+	Align      uint64
 }
 
 func (t *IntType) DefaultArg() Arg {