Skip to content

Commit ef06013

Browse files
committed
Add offset immediate to load/store
1 parent 885bb37 commit ef06013

File tree

8 files changed

+128
-65
lines changed

8 files changed

+128
-65
lines changed

ml-proto/host/lexer.mll

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ let floatop t f32 f64 =
6161
| _ -> assert false
6262

6363
let memop t =
64-
{ty = value_type t; align = None}
64+
{ty = value_type t; offset = 0L; align = None}
6565

6666
let mem_size = function
6767
| "8" -> Memory.Mem8
@@ -109,7 +109,7 @@ let nxx = ixx | fxx
109109
let mixx = "i" ("8" | "16" | "32" | "64")
110110
let mfxx = "f" ("32" | "64")
111111
let sign = "s" | "u"
112-
let align = digit+
112+
let digits = digit+
113113
let mem_size = "8" | "16" | "32"
114114

115115
rule token = parse
@@ -151,7 +151,8 @@ rule token = parse
151151
| (ixx as t)".store"(mem_size as sz)
152152
{ STORE_WRAP (wrapop t sz) }
153153

154-
| "align="(align as a) { ALIGN (int_of_string a) }
154+
| "offset="(digits as s) { OFFSET (Int64.of_string s) }
155+
| "align="(digits as s) { ALIGN (int_of_string s) }
155156

156157
| (nxx as t)".switch" { SWITCH (value_type t) }
157158
| (nxx as t)".const" { CONST (value_type t) }

ml-proto/host/parser.mly

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,20 @@ let literal s t =
4747

4848
(* Memory operands *)
4949

50-
let memop m align =
50+
let memop m offset align =
51+
assert (m.offset = 0L);
5152
assert (m.align = None);
52-
{m with align}
53+
{m with offset; align}
5354

54-
let extop (e : extop) align =
55+
let extop (e : extop) offset align =
56+
assert (e.memop.offset = 0L);
5557
assert (e.memop.align = None);
56-
{e with memop = {e.memop with align}}
58+
{e with memop = {e.memop with offset; align}}
5759

58-
let wrapop (w : wrapop) align =
60+
let wrapop (w : wrapop) offset align =
61+
assert (w.memop.offset = 0L);
5962
assert (w.memop.align = None);
60-
{w with memop = {w.memop with align}}
63+
{w with memop = {w.memop with offset; align}}
6164

6265

6366
(* Symbolic variables *)
@@ -146,7 +149,7 @@ let implicit_decl c t at =
146149
%token INT FLOAT TEXT VAR VALUE_TYPE LPAR RPAR
147150
%token NOP BLOCK IF LOOP LABEL BREAK SWITCH CASE FALLTHROUGH
148151
%token CALL CALL_IMPORT CALL_INDIRECT RETURN
149-
%token GET_LOCAL SET_LOCAL LOAD STORE LOAD_EXTEND STORE_WRAP ALIGN
152+
%token GET_LOCAL SET_LOCAL LOAD STORE LOAD_EXTEND STORE_WRAP OFFSET ALIGN
150153
%token CONST UNARY BINARY COMPARE CONVERT
151154
%token FUNC TYPE PARAM RESULT LOCAL
152155
%token MODULE MEMORY SEGMENT IMPORT EXPORT TABLE
@@ -169,6 +172,7 @@ let implicit_decl c t at =
169172
%token<Ast.memop> STORE
170173
%token<Ast.extop> LOAD_EXTEND
171174
%token<Ast.wrapop> STORE_WRAP
175+
%token<Memory.offset> OFFSET
172176
%token<int> ALIGN
173177

174178
%nonassoc LOW
@@ -221,6 +225,10 @@ labeling :
221225
| bind_var { let at = at () in fun c -> bind_label c $1, Labelled @@ at }
222226
;
223227
228+
offset :
229+
| /* empty */ { 0L }
230+
| OFFSET { $1 }
231+
;
224232
align :
225233
| /* empty */ { None }
226234
| ALIGN { Some $1 }
@@ -254,10 +262,14 @@ expr1 :
254262
{ fun c -> call_indirect ($2 c table, $3 c, $4 c) }
255263
| GET_LOCAL var { fun c -> get_local ($2 c local) }
256264
| SET_LOCAL var expr { fun c -> set_local ($2 c local, $3 c) }
257-
| LOAD align expr { fun c -> load (memop $1 $2, $3 c) }
258-
| STORE align expr expr { fun c -> store (memop $1 $2, $3 c, $4 c) }
259-
| LOAD_EXTEND align expr { fun c -> load_extend (extop $1 $2, $3 c) }
260-
| STORE_WRAP align expr expr { fun c -> store_wrap (wrapop $1 $2, $3 c, $4 c) }
265+
| LOAD offset align expr
266+
{ fun c -> load (memop $1 $2 $3, $4 c) }
267+
| STORE offset align expr expr
268+
{ fun c -> store (memop $1 $2 $3, $4 c, $5 c) }
269+
| LOAD_EXTEND offset align expr
270+
{ fun c -> load_extend (extop $1 $2 $3, $4 c) }
271+
| STORE_WRAP offset align expr expr
272+
{ fun c -> store_wrap (wrapop $1 $2 $3, $4 c, $5 c) }
261273
| CONST literal { fun c -> const (literal $2 $1) }
262274
| UNARY expr { fun c -> unary ($1, $2 c) }
263275
| BINARY expr expr { fun c -> binary ($1, $2 c, $3 c) }

ml-proto/spec/ast.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ type binop = (Int32Op.binop, Int64Op.binop, Float32Op.binop, Float64Op.binop) op
6262
type relop = (Int32Op.relop, Int64Op.relop, Float32Op.relop, Float64Op.relop) op
6363
type cvt = (Int32Op.cvt, Int64Op.cvt, Float32Op.cvt, Float64Op.cvt) op
6464

65-
type memop = {ty : value_type; align : int option}
65+
type memop = {ty : value_type; offset : Memory.offset; align : int option}
6666
type extop = {memop : memop; sz : Memory.mem_size; ext : Memory.extension}
6767
type wrapop = {memop : memop; sz : Memory.mem_size}
6868
type hostop =

ml-proto/spec/check.ml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -232,23 +232,25 @@ and check_case c t et case =
232232

233233
and check_load c et memop e1 at =
234234
check_has_memory c at;
235-
check_align memop.align at;
235+
check_memop memop at;
236236
check_expr c (Some Int32Type) e1;
237237
check_type (Some memop.ty) et at
238238

239239
and check_store c et memop e1 e2 at =
240240
check_has_memory c at;
241-
check_align memop.align at;
241+
check_memop memop at;
242242
check_expr c (Some Int32Type) e1;
243243
check_expr c (Some memop.ty) e2;
244244
check_type (Some memop.ty) et at
245245

246246
and check_has_memory c at =
247247
require c.has_memory at "memory operators require a memory section"
248248

249-
and check_align align at =
250-
Lib.Option.app (fun a ->
251-
require (Lib.Int.is_power_of_two a) at "non-power-of-two alignment") align
249+
and check_memop memop at =
250+
require (memop.offset >= 0L) at "negative offset";
251+
Lib.Option.app
252+
(fun a -> require (Lib.Int.is_power_of_two a) at "non-power-of-two alignment")
253+
memop.align
252254

253255
and check_mem_type ty sz at =
254256
require (ty = Int64Type || sz <> Memory.Mem32) at "memory size too big"

ml-proto/spec/eval.ml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -185,29 +185,32 @@ let rec eval_expr (c : config) (e : expr) =
185185
local c x := v1;
186186
Some v1
187187

188-
| Load ({ty; align = _}, e1) ->
188+
| Load ({ty; offset; align = _}, e1) ->
189189
let mem = some_memory c e.at in
190190
let v1 = mem_size (eval_expr c e1) e1.at in
191-
(try Some (Memory.load mem v1 ty) with exn -> memory_error e.at exn)
191+
(try Some (Memory.load mem v1 offset ty)
192+
with exn -> memory_error e.at exn)
192193

193-
| Store ({ty = _; align = _}, e1, e2) ->
194+
| Store ({ty = _; offset; align = _}, e1, e2) ->
194195
let mem = some_memory c e.at in
195196
let v1 = mem_size (eval_expr c e1) e1.at in
196197
let v2 = some (eval_expr c e2) e2.at in
197-
(try Memory.store mem v1 v2 with exn -> memory_error e.at exn);
198+
(try Memory.store mem v1 offset v2
199+
with exn -> memory_error e.at exn);
198200
Some v2
199201

200-
| LoadExtend ({memop = {ty; align = _}; sz; ext}, e1) ->
202+
| LoadExtend ({memop = {ty; offset; align = _}; sz; ext}, e1) ->
201203
let mem = some_memory c e.at in
202204
let v1 = mem_size (eval_expr c e1) e1.at in
203-
(try Some (Memory.load_extend mem v1 sz ext ty)
205+
(try Some (Memory.load_extend mem v1 offset sz ext ty)
204206
with exn -> memory_error e.at exn)
205207

206-
| StoreWrap ({memop = {ty; align = _}; sz}, e1, e2) ->
208+
| StoreWrap ({memop = {ty; offset; align = _}; sz}, e1, e2) ->
207209
let mem = some_memory c e.at in
208210
let v1 = mem_size (eval_expr c e1) e1.at in
209211
let v2 = some (eval_expr c e2) e2.at in
210-
(try Memory.store_wrap mem v1 sz v2 with exn -> memory_error e.at exn);
212+
(try Memory.store_wrap mem v1 offset sz v2
213+
with exn -> memory_error e.at exn);
211214
Some v2
212215

213216
| Const v ->

ml-proto/spec/memory.ml

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ open Values
88

99
type address = int64
1010
type size = address
11+
type offset = address
1112
type mem_size = Mem8 | Mem16 | Mem32
1213
type extension = SX | ZX
1314
type segment = {addr : address; data : string}
@@ -79,9 +80,13 @@ let grow mem n =
7980
Array1.blit (Array1.sub !mem 0 host_old_size) (Array1.sub after 0 host_old_size);
8081
mem := after
8182

82-
let rec loadn mem n a =
83+
let effective_address a o =
84+
if (Int64.sub Int64.max_int a) < o then raise Bounds;
85+
Int64.add a o
86+
87+
let rec loadn mem n ea =
8388
assert (n > 0 && n <= 8);
84-
let i = host_index_of_int64 a n in
89+
let i = host_index_of_int64 ea n in
8590
try loadn' mem n i with Invalid_argument _ -> raise Bounds
8691

8792
and loadn' mem n i =
@@ -91,55 +96,59 @@ and loadn' mem n i =
9196
else
9297
Int64.logor byte (Int64.shift_left (loadn' mem (n-1) (i+1)) 8)
9398

94-
let rec storen mem n a v =
99+
let rec storen mem n ea v =
95100
assert (n > 0 && n <= 8);
96-
let i = host_index_of_int64 a n in
101+
let i = host_index_of_int64 ea n in
97102
try storen' mem n i v with Invalid_argument _ -> raise Bounds
98103

99104
and storen' mem n i v =
100105
!mem.{i} <- (Int64.to_int v) land 255;
101106
if (n > 1) then
102107
storen' mem (n-1) (i+1) (Int64.shift_right v 8)
103108

104-
let load mem a t =
109+
let load mem a o t =
110+
let ea = effective_address a o in
105111
match t with
106-
| Int32Type -> Int32 (Int64.to_int32 (loadn mem 4 a))
107-
| Int64Type -> Int64 (loadn mem 8 a)
108-
| Float32Type -> Float32 (F32.of_bits (Int64.to_int32 (loadn mem 4 a)))
109-
| Float64Type -> Float64 (F64.of_bits (loadn mem 8 a))
112+
| Int32Type -> Int32 (Int64.to_int32 (loadn mem 4 ea))
113+
| Int64Type -> Int64 (loadn mem 8 ea)
114+
| Float32Type -> Float32 (F32.of_bits (Int64.to_int32 (loadn mem 4 ea)))
115+
| Float64Type -> Float64 (F64.of_bits (loadn mem 8 ea))
110116

111-
let store mem a v =
117+
let store mem a o v =
118+
let ea = effective_address a o in
112119
match v with
113-
| Int32 x -> storen mem 4 a (Int64.of_int32 x)
114-
| Int64 x -> storen mem 8 a x
115-
| Float32 x -> storen mem 4 a (Int64.of_int32 (F32.to_bits x))
116-
| Float64 x -> storen mem 8 a (F64.to_bits x)
120+
| Int32 x -> storen mem 4 ea (Int64.of_int32 x)
121+
| Int64 x -> storen mem 8 ea x
122+
| Float32 x -> storen mem 4 ea (Int64.of_int32 (F32.to_bits x))
123+
| Float64 x -> storen mem 8 ea (F64.to_bits x)
117124

118-
let loadn_sx mem n a =
125+
let loadn_sx mem n ea =
119126
assert (n > 0 && n <= 8);
120-
let v = loadn mem n a in
127+
let v = loadn mem n ea in
121128
let shift = 64 - (8 * n) in
122129
Int64.shift_right (Int64.shift_left v shift) shift
123130

124-
let load_extend mem a sz ext t =
131+
let load_extend mem a o sz ext t =
132+
let ea = effective_address a o in
125133
match sz, ext, t with
126-
| Mem8, ZX, Int32Type -> Int32 (Int64.to_int32 (loadn mem 1 a))
127-
| Mem8, SX, Int32Type -> Int32 (Int64.to_int32 (loadn_sx mem 1 a))
128-
| Mem8, ZX, Int64Type -> Int64 (loadn mem 1 a)
129-
| Mem8, SX, Int64Type -> Int64 (loadn_sx mem 1 a)
130-
| Mem16, ZX, Int32Type -> Int32 (Int64.to_int32 (loadn mem 2 a))
131-
| Mem16, SX, Int32Type -> Int32 (Int64.to_int32 (loadn_sx mem 2 a))
132-
| Mem16, ZX, Int64Type -> Int64 (loadn mem 2 a)
133-
| Mem16, SX, Int64Type -> Int64 (loadn_sx mem 2 a)
134-
| Mem32, ZX, Int64Type -> Int64 (loadn mem 4 a)
135-
| Mem32, SX, Int64Type -> Int64 (loadn_sx mem 4 a)
134+
| Mem8, ZX, Int32Type -> Int32 (Int64.to_int32 (loadn mem 1 ea))
135+
| Mem8, SX, Int32Type -> Int32 (Int64.to_int32 (loadn_sx mem 1 ea))
136+
| Mem8, ZX, Int64Type -> Int64 (loadn mem 1 ea)
137+
| Mem8, SX, Int64Type -> Int64 (loadn_sx mem 1 ea)
138+
| Mem16, ZX, Int32Type -> Int32 (Int64.to_int32 (loadn mem 2 ea))
139+
| Mem16, SX, Int32Type -> Int32 (Int64.to_int32 (loadn_sx mem 2 ea))
140+
| Mem16, ZX, Int64Type -> Int64 (loadn mem 2 ea)
141+
| Mem16, SX, Int64Type -> Int64 (loadn_sx mem 2 ea)
142+
| Mem32, ZX, Int64Type -> Int64 (loadn mem 4 ea)
143+
| Mem32, SX, Int64Type -> Int64 (loadn_sx mem 4 ea)
136144
| _ -> raise Type
137145

138-
let store_wrap mem a sz v =
146+
let store_wrap mem a o sz v =
147+
let ea = effective_address a o in
139148
match sz, v with
140-
| Mem8, Int32 x -> storen mem 1 a (Int64.of_int32 x)
141-
| Mem8, Int64 x -> storen mem 1 a x
142-
| Mem16, Int32 x -> storen mem 2 a (Int64.of_int32 x)
143-
| Mem16, Int64 x -> storen mem 2 a x
144-
| Mem32, Int64 x -> storen mem 4 a x
149+
| Mem8, Int32 x -> storen mem 1 ea (Int64.of_int32 x)
150+
| Mem8, Int64 x -> storen mem 1 ea x
151+
| Mem16, Int32 x -> storen mem 2 ea (Int64.of_int32 x)
152+
| Mem16, Int64 x -> storen mem 2 ea x
153+
| Mem32, Int64 x -> storen mem 4 ea x
145154
| _ -> raise Type

ml-proto/spec/memory.mli

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ type memory
66
type t = memory
77
type address = int64
88
type size = address
9+
type offset = address
910
type mem_size = Mem8 | Mem16 | Mem32
1011
type extension = SX | ZX
1112
type segment = {addr : address; data : string}
@@ -20,8 +21,8 @@ val create : size -> memory
2021
val init : memory -> segment list -> unit
2122
val size : memory -> size
2223
val grow : memory -> size -> unit
23-
val load : memory -> address -> value_type -> value
24-
val store : memory -> address -> value -> unit
24+
val load : memory -> address -> offset -> value_type -> value
25+
val store : memory -> address -> offset -> value -> unit
2526
val load_extend :
26-
memory -> address -> mem_size -> extension -> value_type -> value
27-
val store_wrap : memory -> address -> mem_size -> value -> unit
27+
memory -> address -> offset -> mem_size -> extension -> value_type -> value
28+
val store_wrap : memory -> address -> offset -> mem_size -> value -> unit

ml-proto/test/address.wast

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
(module
2+
(memory 1024 (segment 0 "abcdefghijklmnopqrstuvwxyz"))
3+
(import $print "stdio" "print" (param i32))
4+
5+
(func $good (param $i i32)
6+
(call_import $print (i32.load8_u offset=0 (get_local $i))) ;; 97 'a'
7+
(call_import $print (i32.load8_u offset=1 (get_local $i))) ;; 98 'b'
8+
(call_import $print (i32.load8_u offset=2 (get_local $i))) ;; 99 'c'
9+
(call_import $print (i32.load8_u offset=25 (get_local $i))) ;; 122 'z'
10+
11+
(call_import $print (i32.load16_u offset=0 (get_local $i))) ;; 25185 'ab'
12+
(call_import $print (i32.load16_u offset=1 align=1 (get_local $i))) ;; 25442 'bc'
13+
(call_import $print (i32.load16_u offset=2 (get_local $i))) ;; 25699 'cd'
14+
(call_import $print (i32.load16_u offset=25 align=1 (get_local $i))) ;; 122 'z\0'
15+
16+
(call_import $print (i32.load offset=0 (get_local $i))) ;; 1684234849 'abcd'
17+
(call_import $print (i32.load offset=1 align=1 (get_local $i))) ;; 1701077858 'bcde'
18+
(call_import $print (i32.load offset=2 align=2 (get_local $i))) ;; 1717920867 'cdef'
19+
(call_import $print (i32.load offset=25 align=1 (get_local $i))) ;; 122 'z\0\0\0'
20+
)
21+
(export "good" $good)
22+
23+
(func $bad1 (param $i i32) (i32.load offset=4294967296 (get_local $i)))
24+
(export "bad1" $bad1)
25+
(func $bad2 (param $i i32) (i32.load offset=4294967295 (get_local $i)))
26+
(export "bad2" $bad2)
27+
)
28+
29+
(assert_return (invoke "good" (i32.const 0)))
30+
(assert_return (invoke "good" (i32.const 995)))
31+
(assert_trap (invoke "good" (i32.const 996)) "runtime: out of bounds memory access")
32+
(assert_trap (invoke "bad1" (i32.const 0)) "runtime: out of bounds memory access")
33+
(assert_trap (invoke "bad1" (i32.const 1)) "runtime: out of bounds memory access")
34+
(assert_trap (invoke "bad2" (i32.const 0)) "runtime: out of bounds memory access")
35+
(assert_trap (invoke "bad2" (i32.const 1)) "runtime: out of bounds memory access")

0 commit comments

Comments
 (0)