lib/choir/src/backends/wasm/emission/function/instruction/cast.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const ir = @import("../../../../../core/root.zig");
  3 const dialects = @import("../../../../../dialects/root.zig");
  4 const emission = @import("../../root.zig");
  5 const instruction = @import("root.zig");
  6 
  7 const ScalarKind = dialects.arith.ScalarKind;
  8 
  9 pub fn write(
 10     comptime Out: type,
 11     writer: *instruction.Writer(Out),
 12     operation: *ir.Operation,
 13     bitcast: bool,
 14 ) emission.Error!void {
 15     const operand = operation.getOperand(0) orelse return error.CodeGenFailed;
 16     const result = operation.getResult(0) orelse return error.CodeGenFailed;
 17     const source = emission.types.scalarKindForType(operand.type) orelse
 18         return error.CodeGenFailed;
 19     const target = emission.types.scalarKindForType(result.type) orelse
 20         return error.CodeGenFailed;
 21     try writer.writeValue(operand);
 22     if (!scalarCastIsWasmNoop(source, target)) {
 23         const opcode = if (bitcast)
 24             try bitcastOpcode(source, target)
 25         else
 26             try castOpcode(source, target);
 27         try writer.out.writeByte(opcode);
 28     }
 29     try writer.writeLocalSet(try writer.localFor(result));
 30 }
 31 
 32 fn bitcastOpcode(source: ScalarKind, target: ScalarKind) error{CodeGenFailed}!u8 {
 33     return switch (source) {
 34         .i32, .u32, .index, .bool => switch (target) {
 35             .f32 => 0xbe,
 36             else => error.CodeGenFailed,
 37         },
 38         .i64, .u64 => switch (target) {
 39             .f64 => 0xbf,
 40             else => error.CodeGenFailed,
 41         },
 42         .f32 => switch (target) {
 43             .i32, .u32, .index => 0xbc,
 44             else => error.CodeGenFailed,
 45         },
 46         .f64 => switch (target) {
 47             .i64, .u64 => 0xbd,
 48             else => error.CodeGenFailed,
 49         },
 50         else => error.CodeGenFailed,
 51     };
 52 }
 53 
 54 fn castOpcode(source: ScalarKind, target: ScalarKind) error{CodeGenFailed}!u8 {
 55     return switch (source) {
 56         .i32, .index, .bool => switch (target) {
 57             .i64 => 0xac,
 58             .u64 => 0xad,
 59             .f32 => 0xb2,
 60             .f64 => 0xb7,
 61             else => error.CodeGenFailed,
 62         },
 63         .u32 => switch (target) {
 64             .i64, .u64 => 0xad,
 65             .f32 => 0xb3,
 66             .f64 => 0xb8,
 67             else => error.CodeGenFailed,
 68         },
 69         .i64 => switch (target) {
 70             .i32, .u32, .index => 0xa7,
 71             .f32 => 0xb4,
 72             .f64 => 0xb9,
 73             else => error.CodeGenFailed,
 74         },
 75         .u64 => switch (target) {
 76             .i32, .u32, .index => 0xa7,
 77             .f32 => 0xb5,
 78             .f64 => 0xba,
 79             else => error.CodeGenFailed,
 80         },
 81         .f32 => switch (target) {
 82             .f64 => 0xbb,
 83             else => error.CodeGenFailed,
 84         },
 85         .f64 => switch (target) {
 86             .f32 => 0xb6,
 87             else => error.CodeGenFailed,
 88         },
 89         else => error.CodeGenFailed,
 90     };
 91 }
 92 
 93 fn scalarCastIsWasmNoop(source: ScalarKind, target: ScalarKind) bool {
 94     if (source == target) return true;
 95     return switch (source) {
 96         .i32, .u32, .index, .bool => switch (target) {
 97             .i32, .u32, .index => true,
 98             else => false,
 99         },
100         .i64, .u64 => switch (target) {
101             .i64, .u64 => true,
102             else => false,
103         },
104         else => false,
105     };
106 }
107 
108 test "wasm integer representation casts preserve exact no-op rules" {
109     try std.testing.expect(scalarCastIsWasmNoop(.index, .u32));
110     try std.testing.expect(scalarCastIsWasmNoop(.u32, .i32));
111     try std.testing.expect(scalarCastIsWasmNoop(.i64, .u64));
112     try std.testing.expect(scalarCastIsWasmNoop(.bool, .i32));
113     try std.testing.expect(!scalarCastIsWasmNoop(.i32, .bool));
114     try std.testing.expect(!scalarCastIsWasmNoop(.i32, .i64));
115     try std.testing.expect(!scalarCastIsWasmNoop(.f32, .i32));
116 }