authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-12-06 19:19:52+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-12-12 17:41:59+01:00
log63b69e2c55ae0b41915897f15636f5f6dc4689a5
tree49803ac800f308f0f2a2bd59d1578b94fe034ce4
parent5a6b6992d887ffee1ea64b4f778c5c07ecbaf270
signaturelock-open Commit is signed but in an unrecognized format.

wasm: load+store simd immediate values

This implements loading and storing immediate values representing a vector with exactly 128 bits. When the vector does not equal to 128 bits, or when the simd128 or relaxed-simd features are disabled the value will be treated as an array instead.

3 files changed, 189 insertions(+), 18 deletions(-)

src/arch/wasm/CodeGen.zig+157-15
...@@ -43,6 +43,10 @@ const WValue = union(enum) {...@@ -43,6 +43,10 @@ const WValue = union(enum) {
43 imm32: u32,43 imm32: u32,
44 /// An immediate 64bit value44 /// An immediate 64bit value
45 imm64: u64,45 imm64: u64,
46 /// Index into the list of simd128 immediates. This `WValue` is
47 /// only possible in very rare cases, therefore it would be
48 /// a waste of memory to store the value in a 128 bit integer.
49 imm128: u32,
46 /// A constant 32bit float value50 /// A constant 32bit float value
47 float32: f32,51 float32: f32,
48 /// A constant 64bit float value52 /// A constant 64bit float value
...@@ -116,6 +120,7 @@ const WValue = union(enum) {...@@ -116,6 +120,7 @@ const WValue = union(enum) {
116 .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return,120 .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return,
117 .f32 => gen.free_locals_f32.append(gen.gpa, local_value) catch return,121 .f32 => gen.free_locals_f32.append(gen.gpa, local_value) catch return,
118 .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return,122 .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return,
123 .v128 => gen.free_locals_v128.append(gen.gpa, local_value) catch return,
119 }124 }
120 value.* = undefined;125 value.* = undefined;
121 }126 }
...@@ -258,18 +263,18 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -258,18 +263,18 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
258 8 => switch (args.valtype1.?) {263 8 => switch (args.valtype1.?) {
259 .i32 => if (args.signedness.? == .signed) return .i32_load8_s else return .i32_load8_u,264 .i32 => if (args.signedness.? == .signed) return .i32_load8_s else return .i32_load8_u,
260 .i64 => if (args.signedness.? == .signed) return .i64_load8_s else return .i64_load8_u,265 .i64 => if (args.signedness.? == .signed) return .i64_load8_s else return .i64_load8_u,
261 .f32, .f64 => unreachable,266 .f32, .f64, .v128 => unreachable,
262 },267 },
263 16 => switch (args.valtype1.?) {268 16 => switch (args.valtype1.?) {
264 .i32 => if (args.signedness.? == .signed) return .i32_load16_s else return .i32_load16_u,269 .i32 => if (args.signedness.? == .signed) return .i32_load16_s else return .i32_load16_u,
265 .i64 => if (args.signedness.? == .signed) return .i64_load16_s else return .i64_load16_u,270 .i64 => if (args.signedness.? == .signed) return .i64_load16_s else return .i64_load16_u,
266 .f32, .f64 => unreachable,271 .f32, .f64, .v128 => unreachable,
267 },272 },
268 32 => switch (args.valtype1.?) {273 32 => switch (args.valtype1.?) {
269 .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u,274 .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u,
270 .i32 => return .i32_load,275 .i32 => return .i32_load,
271 .f32 => return .f32_load,276 .f32 => return .f32_load,
272 .f64 => unreachable,277 .f64, .v128 => unreachable,
273 },278 },
274 64 => switch (args.valtype1.?) {279 64 => switch (args.valtype1.?) {
275 .i64 => return .i64_load,280 .i64 => return .i64_load,
...@@ -282,24 +287,25 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -282,24 +287,25 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
282 .i64 => return .i64_load,287 .i64 => return .i64_load,
283 .f32 => return .f32_load,288 .f32 => return .f32_load,
284 .f64 => return .f64_load,289 .f64 => return .f64_load,
290 .v128 => unreachable, // handled independently
285 },291 },
286 .store => if (args.width) |width| {292 .store => if (args.width) |width| {
287 switch (width) {293 switch (width) {
288 8 => switch (args.valtype1.?) {294 8 => switch (args.valtype1.?) {
289 .i32 => return .i32_store8,295 .i32 => return .i32_store8,
290 .i64 => return .i64_store8,296 .i64 => return .i64_store8,
291 .f32, .f64 => unreachable,297 .f32, .f64, .v128 => unreachable,
292 },298 },
293 16 => switch (args.valtype1.?) {299 16 => switch (args.valtype1.?) {
294 .i32 => return .i32_store16,300 .i32 => return .i32_store16,
295 .i64 => return .i64_store16,301 .i64 => return .i64_store16,
296 .f32, .f64 => unreachable,302 .f32, .f64, .v128 => unreachable,
297 },303 },
298 32 => switch (args.valtype1.?) {304 32 => switch (args.valtype1.?) {
299 .i64 => return .i64_store32,305 .i64 => return .i64_store32,
300 .i32 => return .i32_store,306 .i32 => return .i32_store,
301 .f32 => return .f32_store,307 .f32 => return .f32_store,
302 .f64 => unreachable,308 .f64, .v128 => unreachable,
303 },309 },
304 64 => switch (args.valtype1.?) {310 64 => switch (args.valtype1.?) {
305 .i64 => return .i64_store,311 .i64 => return .i64_store,
...@@ -314,6 +320,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -314,6 +320,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
314 .i64 => return .i64_store,320 .i64 => return .i64_store,
315 .f32 => return .f32_store,321 .f32 => return .f32_store,
316 .f64 => return .f64_store,322 .f64 => return .f64_store,
323 .v128 => unreachable, // handled independently
317 }324 }
318 },325 },
319326
...@@ -325,24 +332,27 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -325,24 +332,27 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
325 .i64 => return .i64_const,332 .i64 => return .i64_const,
326 .f32 => return .f32_const,333 .f32 => return .f32_const,
327 .f64 => return .f64_const,334 .f64 => return .f64_const,
335 .v128 => unreachable, // handled independently
328 },336 },
329337
330 .eqz => switch (args.valtype1.?) {338 .eqz => switch (args.valtype1.?) {
331 .i32 => return .i32_eqz,339 .i32 => return .i32_eqz,
332 .i64 => return .i64_eqz,340 .i64 => return .i64_eqz,
333 .f32, .f64 => unreachable,341 .f32, .f64, .v128 => unreachable,
334 },342 },
335 .eq => switch (args.valtype1.?) {343 .eq => switch (args.valtype1.?) {
336 .i32 => return .i32_eq,344 .i32 => return .i32_eq,
337 .i64 => return .i64_eq,345 .i64 => return .i64_eq,
338 .f32 => return .f32_eq,346 .f32 => return .f32_eq,
339 .f64 => return .f64_eq,347 .f64 => return .f64_eq,
348 .v128 => unreachable, // handled independently
340 },349 },
341 .ne => switch (args.valtype1.?) {350 .ne => switch (args.valtype1.?) {
342 .i32 => return .i32_ne,351 .i32 => return .i32_ne,
343 .i64 => return .i64_ne,352 .i64 => return .i64_ne,
344 .f32 => return .f32_ne,353 .f32 => return .f32_ne,
345 .f64 => return .f64_ne,354 .f64 => return .f64_ne,
355 .v128 => unreachable, // handled independently
346 },356 },
347357
348 .lt => switch (args.valtype1.?) {358 .lt => switch (args.valtype1.?) {
...@@ -350,40 +360,47 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -350,40 +360,47 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
350 .i64 => if (args.signedness.? == .signed) return .i64_lt_s else return .i64_lt_u,360 .i64 => if (args.signedness.? == .signed) return .i64_lt_s else return .i64_lt_u,
351 .f32 => return .f32_lt,361 .f32 => return .f32_lt,
352 .f64 => return .f64_lt,362 .f64 => return .f64_lt,
363 .v128 => unreachable, // handled independently
353 },364 },
354 .gt => switch (args.valtype1.?) {365 .gt => switch (args.valtype1.?) {
355 .i32 => if (args.signedness.? == .signed) return .i32_gt_s else return .i32_gt_u,366 .i32 => if (args.signedness.? == .signed) return .i32_gt_s else return .i32_gt_u,
356 .i64 => if (args.signedness.? == .signed) return .i64_gt_s else return .i64_gt_u,367 .i64 => if (args.signedness.? == .signed) return .i64_gt_s else return .i64_gt_u,
357 .f32 => return .f32_gt,368 .f32 => return .f32_gt,
358 .f64 => return .f64_gt,369 .f64 => return .f64_gt,
370 .v128 => unreachable, // handled independently
359 },371 },
360 .le => switch (args.valtype1.?) {372 .le => switch (args.valtype1.?) {
361 .i32 => if (args.signedness.? == .signed) return .i32_le_s else return .i32_le_u,373 .i32 => if (args.signedness.? == .signed) return .i32_le_s else return .i32_le_u,
362 .i64 => if (args.signedness.? == .signed) return .i64_le_s else return .i64_le_u,374 .i64 => if (args.signedness.? == .signed) return .i64_le_s else return .i64_le_u,
363 .f32 => return .f32_le,375 .f32 => return .f32_le,
364 .f64 => return .f64_le,376 .f64 => return .f64_le,
377 .v128 => unreachable, // handled independently
365 },378 },
366 .ge => switch (args.valtype1.?) {379 .ge => switch (args.valtype1.?) {
367 .i32 => if (args.signedness.? == .signed) return .i32_ge_s else return .i32_ge_u,380 .i32 => if (args.signedness.? == .signed) return .i32_ge_s else return .i32_ge_u,
368 .i64 => if (args.signedness.? == .signed) return .i64_ge_s else return .i64_ge_u,381 .i64 => if (args.signedness.? == .signed) return .i64_ge_s else return .i64_ge_u,
369 .f32 => return .f32_ge,382 .f32 => return .f32_ge,
370 .f64 => return .f64_ge,383 .f64 => return .f64_ge,
384 .v128 => unreachable, // handled independently
371 },385 },
372386
373 .clz => switch (args.valtype1.?) {387 .clz => switch (args.valtype1.?) {
374 .i32 => return .i32_clz,388 .i32 => return .i32_clz,
375 .i64 => return .i64_clz,389 .i64 => return .i64_clz,
376 .f32, .f64 => unreachable,390 .f32, .f64 => unreachable,
391 .v128 => unreachable, // handled independently
377 },392 },
378 .ctz => switch (args.valtype1.?) {393 .ctz => switch (args.valtype1.?) {
379 .i32 => return .i32_ctz,394 .i32 => return .i32_ctz,
380 .i64 => return .i64_ctz,395 .i64 => return .i64_ctz,
381 .f32, .f64 => unreachable,396 .f32, .f64 => unreachable,
397 .v128 => unreachable, // handled independently
382 },398 },
383 .popcnt => switch (args.valtype1.?) {399 .popcnt => switch (args.valtype1.?) {
384 .i32 => return .i32_popcnt,400 .i32 => return .i32_popcnt,
385 .i64 => return .i64_popcnt,401 .i64 => return .i64_popcnt,
386 .f32, .f64 => unreachable,402 .f32, .f64 => unreachable,
403 .v128 => unreachable, // handled independently
387 },404 },
388405
389 .add => switch (args.valtype1.?) {406 .add => switch (args.valtype1.?) {
...@@ -391,18 +408,21 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -391,18 +408,21 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
391 .i64 => return .i64_add,408 .i64 => return .i64_add,
392 .f32 => return .f32_add,409 .f32 => return .f32_add,
393 .f64 => return .f64_add,410 .f64 => return .f64_add,
411 .v128 => unreachable, // handled independently
394 },412 },
395 .sub => switch (args.valtype1.?) {413 .sub => switch (args.valtype1.?) {
396 .i32 => return .i32_sub,414 .i32 => return .i32_sub,
397 .i64 => return .i64_sub,415 .i64 => return .i64_sub,
398 .f32 => return .f32_sub,416 .f32 => return .f32_sub,
399 .f64 => return .f64_sub,417 .f64 => return .f64_sub,
418 .v128 => unreachable, // handled independently
400 },419 },
401 .mul => switch (args.valtype1.?) {420 .mul => switch (args.valtype1.?) {
402 .i32 => return .i32_mul,421 .i32 => return .i32_mul,
403 .i64 => return .i64_mul,422 .i64 => return .i64_mul,
404 .f32 => return .f32_mul,423 .f32 => return .f32_mul,
405 .f64 => return .f64_mul,424 .f64 => return .f64_mul,
425 .v128 => unreachable, // handled independently
406 },426 },
407427
408 .div => switch (args.valtype1.?) {428 .div => switch (args.valtype1.?) {
...@@ -410,71 +430,84 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -410,71 +430,84 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
410 .i64 => if (args.signedness.? == .signed) return .i64_div_s else return .i64_div_u,430 .i64 => if (args.signedness.? == .signed) return .i64_div_s else return .i64_div_u,
411 .f32 => return .f32_div,431 .f32 => return .f32_div,
412 .f64 => return .f64_div,432 .f64 => return .f64_div,
433 .v128 => unreachable, // handled independently
413 },434 },
414 .rem => switch (args.valtype1.?) {435 .rem => switch (args.valtype1.?) {
415 .i32 => if (args.signedness.? == .signed) return .i32_rem_s else return .i32_rem_u,436 .i32 => if (args.signedness.? == .signed) return .i32_rem_s else return .i32_rem_u,
416 .i64 => if (args.signedness.? == .signed) return .i64_rem_s else return .i64_rem_u,437 .i64 => if (args.signedness.? == .signed) return .i64_rem_s else return .i64_rem_u,
417 .f32, .f64 => unreachable,438 .f32, .f64 => unreachable,
439 .v128 => unreachable, // handled independently
418 },440 },
419441
420 .@"and" => switch (args.valtype1.?) {442 .@"and" => switch (args.valtype1.?) {
421 .i32 => return .i32_and,443 .i32 => return .i32_and,
422 .i64 => return .i64_and,444 .i64 => return .i64_and,
423 .f32, .f64 => unreachable,445 .f32, .f64 => unreachable,
446 .v128 => unreachable, // handled independently
424 },447 },
425 .@"or" => switch (args.valtype1.?) {448 .@"or" => switch (args.valtype1.?) {
426 .i32 => return .i32_or,449 .i32 => return .i32_or,
427 .i64 => return .i64_or,450 .i64 => return .i64_or,
428 .f32, .f64 => unreachable,451 .f32, .f64 => unreachable,
452 .v128 => unreachable, // handled independently
429 },453 },
430 .xor => switch (args.valtype1.?) {454 .xor => switch (args.valtype1.?) {
431 .i32 => return .i32_xor,455 .i32 => return .i32_xor,
432 .i64 => return .i64_xor,456 .i64 => return .i64_xor,
433 .f32, .f64 => unreachable,457 .f32, .f64 => unreachable,
458 .v128 => unreachable, // handled independently
434 },459 },
435460
436 .shl => switch (args.valtype1.?) {461 .shl => switch (args.valtype1.?) {
437 .i32 => return .i32_shl,462 .i32 => return .i32_shl,
438 .i64 => return .i64_shl,463 .i64 => return .i64_shl,
439 .f32, .f64 => unreachable,464 .f32, .f64 => unreachable,
465 .v128 => unreachable, // handled independently
440 },466 },
441 .shr => switch (args.valtype1.?) {467 .shr => switch (args.valtype1.?) {
442 .i32 => if (args.signedness.? == .signed) return .i32_shr_s else return .i32_shr_u,468 .i32 => if (args.signedness.? == .signed) return .i32_shr_s else return .i32_shr_u,
443 .i64 => if (args.signedness.? == .signed) return .i64_shr_s else return .i64_shr_u,469 .i64 => if (args.signedness.? == .signed) return .i64_shr_s else return .i64_shr_u,
444 .f32, .f64 => unreachable,470 .f32, .f64 => unreachable,
471 .v128 => unreachable, // handled independently
445 },472 },
446 .rotl => switch (args.valtype1.?) {473 .rotl => switch (args.valtype1.?) {
447 .i32 => return .i32_rotl,474 .i32 => return .i32_rotl,
448 .i64 => return .i64_rotl,475 .i64 => return .i64_rotl,
449 .f32, .f64 => unreachable,476 .f32, .f64 => unreachable,
477 .v128 => unreachable, // handled independently
450 },478 },
451 .rotr => switch (args.valtype1.?) {479 .rotr => switch (args.valtype1.?) {
452 .i32 => return .i32_rotr,480 .i32 => return .i32_rotr,
453 .i64 => return .i64_rotr,481 .i64 => return .i64_rotr,
454 .f32, .f64 => unreachable,482 .f32, .f64 => unreachable,
483 .v128 => unreachable, // handled independently
455 },484 },
456485
457 .abs => switch (args.valtype1.?) {486 .abs => switch (args.valtype1.?) {
458 .i32, .i64 => unreachable,487 .i32, .i64 => unreachable,
459 .f32 => return .f32_abs,488 .f32 => return .f32_abs,
460 .f64 => return .f64_abs,489 .f64 => return .f64_abs,
490 .v128 => unreachable, // handled independently
461 },491 },
462 .neg => switch (args.valtype1.?) {492 .neg => switch (args.valtype1.?) {
463 .i32, .i64 => unreachable,493 .i32, .i64 => unreachable,
464 .f32 => return .f32_neg,494 .f32 => return .f32_neg,
465 .f64 => return .f64_neg,495 .f64 => return .f64_neg,
496 .v128 => unreachable, // handled independently
466 },497 },
467 .ceil => switch (args.valtype1.?) {498 .ceil => switch (args.valtype1.?) {
468 .i64 => unreachable,499 .i64 => unreachable,
469 .i32 => return .f32_ceil, // when valtype is f16, we store it in i32.500 .i32 => return .f32_ceil, // when valtype is f16, we store it in i32.
470 .f32 => return .f32_ceil,501 .f32 => return .f32_ceil,
471 .f64 => return .f64_ceil,502 .f64 => return .f64_ceil,
503 .v128 => unreachable, // handled independently
472 },504 },
473 .floor => switch (args.valtype1.?) {505 .floor => switch (args.valtype1.?) {
474 .i64 => unreachable,506 .i64 => unreachable,
475 .i32 => return .f32_floor, // when valtype is f16, we store it in i32.507 .i32 => return .f32_floor, // when valtype is f16, we store it in i32.
476 .f32 => return .f32_floor,508 .f32 => return .f32_floor,
477 .f64 => return .f64_floor,509 .f64 => return .f64_floor,
510 .v128 => unreachable, // handled independently
478 },511 },
479 .trunc => switch (args.valtype1.?) {512 .trunc => switch (args.valtype1.?) {
480 .i32 => if (args.valtype2) |valty| switch (valty) {513 .i32 => if (args.valtype2) |valty| switch (valty) {
...@@ -482,40 +515,48 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -482,40 +515,48 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
482 .i64 => unreachable,515 .i64 => unreachable,
483 .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u,516 .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u,
484 .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u,517 .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u,
518 .v128 => unreachable, // handled independently
485 } else return .f32_trunc, // when no valtype2, it's an f16 instead which is stored in an i32.519 } else return .f32_trunc, // when no valtype2, it's an f16 instead which is stored in an i32.
486 .i64 => switch (args.valtype2.?) {520 .i64 => switch (args.valtype2.?) {
487 .i32 => unreachable,521 .i32 => unreachable,
488 .i64 => unreachable,522 .i64 => unreachable,
489 .f32 => if (args.signedness.? == .signed) return .i64_trunc_f32_s else return .i64_trunc_f32_u,523 .f32 => if (args.signedness.? == .signed) return .i64_trunc_f32_s else return .i64_trunc_f32_u,
490 .f64 => if (args.signedness.? == .signed) return .i64_trunc_f64_s else return .i64_trunc_f64_u,524 .f64 => if (args.signedness.? == .signed) return .i64_trunc_f64_s else return .i64_trunc_f64_u,
525 .v128 => unreachable, // handled independently
491 },526 },
492 .f32 => return .f32_trunc,527 .f32 => return .f32_trunc,
493 .f64 => return .f64_trunc,528 .f64 => return .f64_trunc,
529 .v128 => unreachable, // handled independently
494 },530 },
495 .nearest => switch (args.valtype1.?) {531 .nearest => switch (args.valtype1.?) {
496 .i32, .i64 => unreachable,532 .i32, .i64 => unreachable,
497 .f32 => return .f32_nearest,533 .f32 => return .f32_nearest,
498 .f64 => return .f64_nearest,534 .f64 => return .f64_nearest,
535 .v128 => unreachable, // handled independently
499 },536 },
500 .sqrt => switch (args.valtype1.?) {537 .sqrt => switch (args.valtype1.?) {
501 .i32, .i64 => unreachable,538 .i32, .i64 => unreachable,
502 .f32 => return .f32_sqrt,539 .f32 => return .f32_sqrt,
503 .f64 => return .f64_sqrt,540 .f64 => return .f64_sqrt,
541 .v128 => unreachable, // handled independently
504 },542 },
505 .min => switch (args.valtype1.?) {543 .min => switch (args.valtype1.?) {
506 .i32, .i64 => unreachable,544 .i32, .i64 => unreachable,
507 .f32 => return .f32_min,545 .f32 => return .f32_min,
508 .f64 => return .f64_min,546 .f64 => return .f64_min,
547 .v128 => unreachable, // handled independently
509 },548 },
510 .max => switch (args.valtype1.?) {549 .max => switch (args.valtype1.?) {
511 .i32, .i64 => unreachable,550 .i32, .i64 => unreachable,
512 .f32 => return .f32_max,551 .f32 => return .f32_max,
513 .f64 => return .f64_max,552 .f64 => return .f64_max,
553 .v128 => unreachable, // handled independently
514 },554 },
515 .copysign => switch (args.valtype1.?) {555 .copysign => switch (args.valtype1.?) {
516 .i32, .i64 => unreachable,556 .i32, .i64 => unreachable,
517 .f32 => return .f32_copysign,557 .f32 => return .f32_copysign,
518 .f64 => return .f64_copysign,558 .f64 => return .f64_copysign,
559 .v128 => unreachable, // handled independently
519 },560 },
520561
521 .wrap => switch (args.valtype1.?) {562 .wrap => switch (args.valtype1.?) {
...@@ -523,8 +564,10 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -523,8 +564,10 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
523 .i32 => unreachable,564 .i32 => unreachable,
524 .i64 => return .i32_wrap_i64,565 .i64 => return .i32_wrap_i64,
525 .f32, .f64 => unreachable,566 .f32, .f64 => unreachable,
567 .v128 => unreachable, // handled independently
526 },568 },
527 .i64, .f32, .f64 => unreachable,569 .i64, .f32, .f64 => unreachable,
570 .v128 => unreachable, // handled independently
528 },571 },
529 .convert => switch (args.valtype1.?) {572 .convert => switch (args.valtype1.?) {
530 .i32, .i64 => unreachable,573 .i32, .i64 => unreachable,
...@@ -532,12 +575,15 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -532,12 +575,15 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
532 .i32 => if (args.signedness.? == .signed) return .f32_convert_i32_s else return .f32_convert_i32_u,575 .i32 => if (args.signedness.? == .signed) return .f32_convert_i32_s else return .f32_convert_i32_u,
533 .i64 => if (args.signedness.? == .signed) return .f32_convert_i64_s else return .f32_convert_i64_u,576 .i64 => if (args.signedness.? == .signed) return .f32_convert_i64_s else return .f32_convert_i64_u,
534 .f32, .f64 => unreachable,577 .f32, .f64 => unreachable,
578 .v128 => unreachable, // handled independently
535 },579 },
536 .f64 => switch (args.valtype2.?) {580 .f64 => switch (args.valtype2.?) {
537 .i32 => if (args.signedness.? == .signed) return .f64_convert_i32_s else return .f64_convert_i32_u,581 .i32 => if (args.signedness.? == .signed) return .f64_convert_i32_s else return .f64_convert_i32_u,
538 .i64 => if (args.signedness.? == .signed) return .f64_convert_i64_s else return .f64_convert_i64_u,582 .i64 => if (args.signedness.? == .signed) return .f64_convert_i64_s else return .f64_convert_i64_u,
539 .f32, .f64 => unreachable,583 .f32, .f64 => unreachable,
584 .v128 => unreachable, // handled independently
540 },585 },
586 .v128 => unreachable, // handled independently
541 },587 },
542 .demote => if (args.valtype1.? == .f32 and args.valtype2.? == .f64) return .f32_demote_f64 else unreachable,588 .demote => if (args.valtype1.? == .f32 and args.valtype2.? == .f64) return .f32_demote_f64 else unreachable,
543 .promote => if (args.valtype1.? == .f64 and args.valtype2.? == .f32) return .f64_promote_f32 else unreachable,589 .promote => if (args.valtype1.? == .f64 and args.valtype2.? == .f32) return .f64_promote_f32 else unreachable,
...@@ -546,6 +592,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -546,6 +592,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
546 .i64 => if (args.valtype2.? == .f64) return .i64_reinterpret_f64 else unreachable,592 .i64 => if (args.valtype2.? == .f64) return .i64_reinterpret_f64 else unreachable,
547 .f32 => if (args.valtype2.? == .i32) return .f32_reinterpret_i32 else unreachable,593 .f32 => if (args.valtype2.? == .i32) return .f32_reinterpret_i32 else unreachable,
548 .f64 => if (args.valtype2.? == .i64) return .f64_reinterpret_i64 else unreachable,594 .f64 => if (args.valtype2.? == .i64) return .f64_reinterpret_i64 else unreachable,
595 .v128 => unreachable, // handled independently
549 },596 },
550 .extend => switch (args.valtype1.?) {597 .extend => switch (args.valtype1.?) {
551 .i32 => switch (args.width.?) {598 .i32 => switch (args.width.?) {
...@@ -560,6 +607,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -560,6 +607,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
560 else => unreachable,607 else => unreachable,
561 },608 },
562 .f32, .f64 => unreachable,609 .f32, .f64 => unreachable,
610 .v128 => unreachable, // handled independently
563 },611 },
564 }612 }
565}613}
...@@ -629,6 +677,10 @@ err_msg: *Module.ErrorMsg,...@@ -629,6 +677,10 @@ err_msg: *Module.ErrorMsg,
629/// List of all locals' types generated throughout this declaration677/// List of all locals' types generated throughout this declaration
630/// used to emit locals count at start of 'code' section.678/// used to emit locals count at start of 'code' section.
631locals: std.ArrayListUnmanaged(u8),679locals: std.ArrayListUnmanaged(u8),
680/// List of simd128 immediates. Each value is stored as an array of bytes.
681/// This list will only be populated for 128bit-simd values when the target features
682/// are enabled also.
683simd_immediates: std.ArrayListUnmanaged([16]u8) = .{},
632/// The Target we're emitting (used to call intInfo)684/// The Target we're emitting (used to call intInfo)
633target: std.Target,685target: std.Target,
634/// Represents the wasm binary file that is being linked.686/// Represents the wasm binary file that is being linked.
...@@ -665,14 +717,17 @@ stack_alignment: u32 = 16,...@@ -665,14 +717,17 @@ stack_alignment: u32 = 16,
665/// It is illegal to store a non-i32 valtype in this list.717/// It is illegal to store a non-i32 valtype in this list.
666free_locals_i32: std.ArrayListUnmanaged(u32) = .{},718free_locals_i32: std.ArrayListUnmanaged(u32) = .{},
667/// A list of indexes which represents a local of valtype `i64`.719/// A list of indexes which represents a local of valtype `i64`.
668/// It is illegal to store a non-i32 valtype in this list.720/// It is illegal to store a non-i64 valtype in this list.
669free_locals_i64: std.ArrayListUnmanaged(u32) = .{},721free_locals_i64: std.ArrayListUnmanaged(u32) = .{},
670/// A list of indexes which represents a local of valtype `f32`.722/// A list of indexes which represents a local of valtype `f32`.
671/// It is illegal to store a non-i32 valtype in this list.723/// It is illegal to store a non-f32 valtype in this list.
672free_locals_f32: std.ArrayListUnmanaged(u32) = .{},724free_locals_f32: std.ArrayListUnmanaged(u32) = .{},
673/// A list of indexes which represents a local of valtype `f64`.725/// A list of indexes which represents a local of valtype `f64`.
674/// It is illegal to store a non-i32 valtype in this list.726/// It is illegal to store a non-f64 valtype in this list.
675free_locals_f64: std.ArrayListUnmanaged(u32) = .{},727free_locals_f64: std.ArrayListUnmanaged(u32) = .{},
728/// A list of indexes which represents a local of valtype `v127`.
729/// It is illegal to store a non-v128 valtype in this list.
730free_locals_v128: std.ArrayListUnmanaged(u32) = .{},
676731
677/// When in debug mode, this tracks if no `finishAir` was missed.732/// When in debug mode, this tracks if no `finishAir` was missed.
678/// Forgetting to call `finishAir` will cause the result to not be733/// Forgetting to call `finishAir` will cause the result to not be
...@@ -699,12 +754,14 @@ pub fn deinit(func: *CodeGen) void {...@@ -699,12 +754,14 @@ pub fn deinit(func: *CodeGen) void {
699 func.branches.deinit(func.gpa);754 func.branches.deinit(func.gpa);
700 func.blocks.deinit(func.gpa);755 func.blocks.deinit(func.gpa);
701 func.locals.deinit(func.gpa);756 func.locals.deinit(func.gpa);
757 func.simd_immediates.deinit(func.gpa);
702 func.mir_instructions.deinit(func.gpa);758 func.mir_instructions.deinit(func.gpa);
703 func.mir_extra.deinit(func.gpa);759 func.mir_extra.deinit(func.gpa);
704 func.free_locals_i32.deinit(func.gpa);760 func.free_locals_i32.deinit(func.gpa);
705 func.free_locals_i64.deinit(func.gpa);761 func.free_locals_i64.deinit(func.gpa);
706 func.free_locals_f32.deinit(func.gpa);762 func.free_locals_f32.deinit(func.gpa);
707 func.free_locals_f64.deinit(func.gpa);763 func.free_locals_f64.deinit(func.gpa);
764 func.free_locals_v128.deinit(func.gpa);
708 func.* = undefined;765 func.* = undefined;
709}766}
710767
...@@ -867,6 +924,17 @@ fn addImm64(func: *CodeGen, imm: u64) error{OutOfMemory}!void {...@@ -867,6 +924,17 @@ fn addImm64(func: *CodeGen, imm: u64) error{OutOfMemory}!void {
867 try func.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } });924 try func.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } });
868}925}
869926
927/// Accepts the index into the list of 128bit-immediates
928fn addImm128(func: *CodeGen, index: u32) error{OutOfMemory}!void {
929 const simd_values = func.simd_immediates.items[index];
930 const extra_index = @intCast(u32, func.mir_extra.items.len);
931 // tag + 128bit value
932 try func.mir_extra.ensureUnusedCapacity(func.gpa, 5);
933 func.mir_extra.appendAssumeCapacity(std.wasm.simdOpcode(.v128_const));
934 func.mir_extra.appendSliceAssumeCapacity(@alignCast(4, mem.bytesAsSlice(u32, &simd_values)));
935 try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
936}
937
870fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void {938fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void {
871 const extra_index = try func.addExtra(Mir.Float64.fromFloat64(float));939 const extra_index = try func.addExtra(Mir.Float64.fromFloat64(float));
872 try func.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });940 try func.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });
...@@ -924,6 +992,10 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype {...@@ -924,6 +992,10 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype {
924 },992 },
925 else => wasm.Valtype.i32,993 else => wasm.Valtype.i32,
926 },994 },
995 .Vector => switch (determineSimdStoreStrategy(ty, target)) {
996 .direct => wasm.Valtype.v128,
997 .unrolled => wasm.Valtype.i32,
998 },
927 else => wasm.Valtype.i32, // all represented as reference/immediate999 else => wasm.Valtype.i32, // all represented as reference/immediate
928 };1000 };
929}1001}
...@@ -950,6 +1022,7 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {...@@ -950,6 +1022,7 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {
950 .local => |idx| try func.addLabel(.local_get, idx.value),1022 .local => |idx| try func.addLabel(.local_get, idx.value),
951 .imm32 => |val| try func.addImm32(@bitCast(i32, val)),1023 .imm32 => |val| try func.addImm32(@bitCast(i32, val)),
952 .imm64 => |val| try func.addImm64(val),1024 .imm64 => |val| try func.addImm64(val),
1025 .imm128 => |val| try func.addImm128(val),
953 .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),1026 .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
954 .float64 => |val| try func.addFloat64(val),1027 .float64 => |val| try func.addFloat64(val),
955 .memory => |ptr| {1028 .memory => |ptr| {
...@@ -1016,6 +1089,10 @@ fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue {...@@ -1016,6 +1089,10 @@ fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue {
1016 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });1089 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
1017 return WValue{ .local = .{ .value = index, .references = 1 } };1090 return WValue{ .local = .{ .value = index, .references = 1 } };
1018 },1091 },
1092 .v128 => if (func.free_locals_v128.popOrNull()) |index| {
1093 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
1094 return WValue{ .local = .{ .value = index, .references = 1 } };
1095 },
1019 }1096 }
1020 log.debug("new local of type {}\n", .{valtype});1097 log.debug("new local of type {}\n", .{valtype});
1021 // no local was free to be re-used, so allocate a new local instead1098 // no local was free to be re-used, so allocate a new local instead
...@@ -1098,7 +1175,6 @@ pub fn generate(...@@ -1098,7 +1175,6 @@ pub fn generate(
1098 .gpa = bin_file.allocator,1175 .gpa = bin_file.allocator,
1099 .air = air,1176 .air = air,
1100 .liveness = liveness,1177 .liveness = liveness,
1101 // .values = .{},
1102 .code = code,1178 .code = code,
1103 .decl_index = func.owner_decl,1179 .decl_index = func.owner_decl,
1104 .decl = bin_file.options.module.?.declPtr(func.owner_decl),1180 .decl = bin_file.options.module.?.declPtr(func.owner_decl),
...@@ -1481,9 +1557,9 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {...@@ -1481,9 +1557,9 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
1481 .imm64 => |val| val,1557 .imm64 => |val| val,
1482 else => unreachable,1558 else => unreachable,
1483 };1559 };
1484 // if the size (length) is more than 1024 bytes, we use a runtime loop instead to prevent1560 // if the size (length) is more than 32 bytes, we use a runtime loop instead to prevent
1485 // binary size bloat.1561 // binary size bloat.
1486 if (length > 1024) break :blk;1562 if (length > 32) break :blk;
1487 var offset: u32 = 0;1563 var offset: u32 = 0;
1488 const lhs_base = dst.offset();1564 const lhs_base = dst.offset();
1489 const rhs_base = src.offset();1565 const rhs_base = src.offset();
...@@ -1612,7 +1688,6 @@ fn isByRef(ty: Type, target: std.Target) bool {...@@ -1612,7 +1688,6 @@ fn isByRef(ty: Type, target: std.Target) bool {
1612 => return false,1688 => return false,
16131689
1614 .Array,1690 .Array,
1615 .Vector,
1616 .Frame,1691 .Frame,
1617 .Union,1692 .Union,
1618 => return ty.hasRuntimeBitsIgnoreComptime(),1693 => return ty.hasRuntimeBitsIgnoreComptime(),
...@@ -1625,6 +1700,7 @@ fn isByRef(ty: Type, target: std.Target) bool {...@@ -1625,6 +1700,7 @@ fn isByRef(ty: Type, target: std.Target) bool {
1625 }1700 }
1626 return ty.hasRuntimeBitsIgnoreComptime();1701 return ty.hasRuntimeBitsIgnoreComptime();
1627 },1702 },
1703 .Vector => return determineSimdStoreStrategy(ty, target) == .unrolled,
1628 .Int => return ty.intInfo(target).bits > 64,1704 .Int => return ty.intInfo(target).bits > 64,
1629 .Float => return ty.floatBits(target) > 64,1705 .Float => return ty.floatBits(target) > 64,
1630 .ErrorUnion => {1706 .ErrorUnion => {
...@@ -1647,6 +1723,26 @@ fn isByRef(ty: Type, target: std.Target) bool {...@@ -1647,6 +1723,26 @@ fn isByRef(ty: Type, target: std.Target) bool {
1647 }1723 }
1648}1724}
16491725
1726const SimdStoreStrategy = enum {
1727 direct,
1728 unrolled,
1729};
1730
1731/// For a given vector type, returns the `SimdStoreStrategy`.
1732/// This means when a given type is 128 bits and either the simd128 or relaxed-simd
1733/// features are enabled, the function will return `.direct`. This would allow to store
1734/// it using a instruction, rather than an unrolled version.
1735fn determineSimdStoreStrategy(ty: Type, target: std.Target) SimdStoreStrategy {
1736 std.debug.assert(ty.zigTypeTag() == .Vector);
1737 if (ty.bitSize(target) != 128) return .unrolled;
1738 const hasFeature = std.Target.wasm.featureSetHas;
1739 const features = target.cpu.features;
1740 if (hasFeature(features, .relaxed_simd) or hasFeature(features, .simd128)) {
1741 return .direct;
1742 }
1743 return .unrolled;
1744}
1745
1650/// Creates a new local for a pointer that points to memory with given offset.1746/// Creates a new local for a pointer that points to memory with given offset.
1651/// This can be used to get a pointer to a struct field, error payload, etc.1747/// This can be used to get a pointer to a struct field, error payload, etc.
1652/// By providing `modify` as action, it will modify the given `ptr_value` instead of making a new1748/// By providing `modify` as action, it will modify the given `ptr_value` instead of making a new
...@@ -2187,10 +2283,29 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE...@@ -2187,10 +2283,29 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE
2187 const len = @intCast(u32, ty.abiSize(func.target));2283 const len = @intCast(u32, ty.abiSize(func.target));
2188 return func.memcpy(lhs, rhs, .{ .imm32 = len });2284 return func.memcpy(lhs, rhs, .{ .imm32 = len });
2189 },2285 },
2190 .Struct, .Array, .Union, .Vector => if (isByRef(ty, func.target)) {2286 .Struct, .Array, .Union => if (isByRef(ty, func.target)) {
2191 const len = @intCast(u32, ty.abiSize(func.target));2287 const len = @intCast(u32, ty.abiSize(func.target));
2192 return func.memcpy(lhs, rhs, .{ .imm32 = len });2288 return func.memcpy(lhs, rhs, .{ .imm32 = len });
2193 },2289 },
2290 .Vector => switch (determineSimdStoreStrategy(ty, func.target)) {
2291 .unrolled => {
2292 const len = @intCast(u32, ty.abiSize(func.target));
2293 return func.memcpy(lhs, rhs, .{ .imm32 = len });
2294 },
2295 .direct => {
2296 try func.emitWValue(lhs);
2297 try func.lowerToStack(rhs);
2298 // TODO: Add helper functions for simd opcodes
2299 const extra_index = @intCast(u32, func.mir_extra.items.len);
2300 // stores as := opcode, offset, alignment (opcode::memarg)
2301 try func.mir_extra.appendSlice(func.gpa, &[_]u32{
2302 std.wasm.simdOpcode(.v128_store),
2303 offset + lhs.offset(),
2304 ty.abiAlignment(func.target),
2305 });
2306 return func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
2307 },
2308 },
2194 .Pointer => {2309 .Pointer => {
2195 if (ty.isSlice()) {2310 if (ty.isSlice()) {
2196 // store pointer first2311 // store pointer first
...@@ -2289,6 +2404,19 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu...@@ -2289,6 +2404,19 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu
2289 // load local's value from memory by its stack position2404 // load local's value from memory by its stack position
2290 try func.emitWValue(operand);2405 try func.emitWValue(operand);
22912406
2407 if (ty.zigTypeTag() == .Vector) {
2408 // TODO: Add helper functions for simd opcodes
2409 const extra_index = @intCast(u32, func.mir_extra.items.len);
2410 // stores as := opcode, offset, alignment (opcode::memarg)
2411 try func.mir_extra.appendSlice(func.gpa, &[_]u32{
2412 std.wasm.simdOpcode(.v128_load),
2413 offset + operand.offset(),
2414 ty.abiAlignment(func.target),
2415 });
2416 try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } });
2417 return WValue{ .stack = {} };
2418 }
2419
2292 const abi_size = @intCast(u8, ty.abiSize(func.target));2420 const abi_size = @intCast(u8, ty.abiSize(func.target));
2293 const opcode = buildOpcode(.{2421 const opcode = buildOpcode(.{
2294 .valtype1 = typeToValtype(ty, func.target),2422 .valtype1 = typeToValtype(ty, func.target),
...@@ -2766,10 +2894,24 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {...@@ -2766,10 +2894,24 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
2766 const int_val = Value.initPayload(&payload.base);2894 const int_val = Value.initPayload(&payload.base);
2767 return func.lowerConstant(int_val, struct_obj.backing_int_ty);2895 return func.lowerConstant(int_val, struct_obj.backing_int_ty);
2768 },2896 },
2897 .Vector => {
2898 assert(determineSimdStoreStrategy(ty, target) == .direct);
2899 var buf: [16]u8 = undefined;
2900 val.writeToMemory(ty, func.bin_file.base.options.module.?, &buf);
2901 return func.storeSimdImmd(buf);
2902 },
2769 else => |zig_type| return func.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}),2903 else => |zig_type| return func.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}),
2770 }2904 }
2771}2905}
27722906
2907/// Stores the value as a 128bit-immediate value by storing it inside
2908/// the list and returning the index into this list as `WValue`.
2909fn storeSimdImmd(func: *CodeGen, value: [16]u8) !WValue {
2910 const index = @intCast(u32, func.simd_immediates.items.len);
2911 try func.simd_immediates.append(func.gpa, value);
2912 return WValue{ .imm128 = index };
2913}
2914
2773fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {2915fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {
2774 switch (ty.zigTypeTag()) {2916 switch (ty.zigTypeTag()) {
2775 .Bool, .ErrorSet => return WValue{ .imm32 = 0xaaaaaaaa },2917 .Bool, .ErrorSet => return WValue{ .imm32 = 0xaaaaaaaa },
src/arch/wasm/Emit.zig+25-2
...@@ -240,6 +240,7 @@ pub fn emitMir(emit: *Emit) InnerError!void {...@@ -240,6 +240,7 @@ pub fn emitMir(emit: *Emit) InnerError!void {
240 .i64_ctz => try emit.emitTag(tag),240 .i64_ctz => try emit.emitTag(tag),
241241
242 .extended => try emit.emitExtended(inst),242 .extended => try emit.emitExtended(inst),
243 .simd => try emit.emitSimd(inst),
243 }244 }
244 }245 }
245}246}
...@@ -341,11 +342,14 @@ fn emitMemArg(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {...@@ -341,11 +342,14 @@ fn emitMemArg(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
341 const extra_index = emit.mir.instructions.items(.data)[inst].payload;342 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
342 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index).data;343 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index).data;
343 try emit.code.append(@enumToInt(tag));344 try emit.code.append(@enumToInt(tag));
345 try encodeMemArg(mem_arg, emit.code.writer());
346}
344347
348fn encodeMemArg(mem_arg: Mir.MemArg, writer: anytype) !void {
345 // wasm encodes alignment as power of 2, rather than natural alignment349 // wasm encodes alignment as power of 2, rather than natural alignment
346 const encoded_alignment = @ctz(mem_arg.alignment);350 const encoded_alignment = @ctz(mem_arg.alignment);
347 try leb128.writeULEB128(emit.code.writer(), encoded_alignment);351 try leb128.writeULEB128(writer, encoded_alignment);
348 try leb128.writeULEB128(emit.code.writer(), mem_arg.offset);352 try leb128.writeULEB128(writer, mem_arg.offset);
349}353}
350354
351fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {355fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
...@@ -426,6 +430,25 @@ fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -426,6 +430,25 @@ fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {
426 }430 }
427}431}
428432
433fn emitSimd(emit: *Emit, inst: Mir.Inst.Index) !void {
434 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
435 const opcode = emit.mir.extra[extra_index];
436 const writer = emit.code.writer();
437 try emit.code.append(0xFD);
438 try leb128.writeULEB128(writer, opcode);
439 switch (@intToEnum(std.wasm.SimdOpcode, opcode)) {
440 .v128_store, .v128_load => {
441 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index + 1).data;
442 try encodeMemArg(mem_arg, writer);
443 },
444 .v128_const => {
445 const simd_value = emit.mir.extra[extra_index + 1 ..][0..4];
446 try writer.writeAll(std.mem.asBytes(simd_value));
447 },
448 else => |tag| return emit.fail("TODO: Implement simd instruction: {s}\n", .{@tagName(tag)}),
449 }
450}
451
429fn emitMemFill(emit: *Emit) !void {452fn emitMemFill(emit: *Emit) !void {
430 try emit.code.append(0xFC);453 try emit.code.append(0xFC);
431 try emit.code.append(0x0B);454 try emit.code.append(0x0B);
src/arch/wasm/Mir.zig+7-1
...@@ -518,6 +518,12 @@ pub const Inst = struct {...@@ -518,6 +518,12 @@ pub const Inst = struct {
518 ///518 ///
519 /// The `data` field depends on the extension instruction519 /// The `data` field depends on the extension instruction
520 extended = 0xFC,520 extended = 0xFC,
521 /// The instruction consists of a simd opcode.
522 /// The actual simd-opcode is found at payload's index.
523 ///
524 /// The `data` field depends on the simd instruction and
525 /// may contain additional data.
526 simd = 0xFD,
521 /// Contains a symbol to a function pointer527 /// Contains a symbol to a function pointer
522 /// uses `label`528 /// uses `label`
523 ///529 ///
...@@ -578,7 +584,7 @@ pub fn deinit(self: *Mir, gpa: std.mem.Allocator) void {...@@ -578,7 +584,7 @@ pub fn deinit(self: *Mir, gpa: std.mem.Allocator) void {
578 self.* = undefined;584 self.* = undefined;
579}585}
580586
581pub fn extraData(self: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {587pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
582 const fields = std.meta.fields(T);588 const fields = std.meta.fields(T);
583 var i: usize = index;589 var i: usize = index;
584 var result: T = undefined;590 var result: T = undefined;