lib/choir/src/backends/wasm/emission/function/instruction/arithmetic/lowering.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const wasm = @import("../../../../root.zig");
  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 const arithmetic = @import("root.zig");
  7 
  8 const ArithDialect = dialects.ArithDialect;
  9 
 10 const UnaryKind = enum {
 11     neg,
 12     abs,
 13     sqrt,
 14 };
 15 
 16 pub fn writeConstant(
 17     comptime Out: type,
 18     writer: *instruction.Writer(Out),
 19     operation: *ir.Operation,
 20 ) emission.Error!void {
 21     const constant = ArithDialect.ConstantOp{ .op = operation };
 22     const result = constant.getResult();
 23     const scalar = emission.types.scalarKindForType(result.type) orelse
 24         return error.CodeGenFailed;
 25     switch (scalar) {
 26         .bool => {
 27             const attr = operation.getAttrAs(ir.Attribute.BoolAttr, "value") orelse
 28                 return error.CodeGenFailed;
 29             try writer.out.writeByte(0x41);
 30             try wasm.binary.writeSleb(writer.out, if (attr.getValue()) 1 else 0);
 31         },
 32         .i8, .i16, .i32, .u8, .u16, .u32, .index => {
 33             const value = constant.getIntValue() orelse return error.CodeGenFailed;
 34             try writer.out.writeByte(0x41);
 35             try wasm.binary.writeSleb(writer.out, @intCast(value));
 36         },
 37         .i64, .u64 => {
 38             const value = constant.getIntValue() orelse return error.CodeGenFailed;
 39             try writer.out.writeByte(0x42);
 40             try wasm.binary.writeSleb(writer.out, value);
 41         },
 42         .f32 => {
 43             const attr = operation.getAttrAs(ir.Attribute.FloatAttr, "value") orelse
 44                 return error.CodeGenFailed;
 45             try writer.out.writeByte(0x43);
 46             try wasm.binary.writeF32(writer.out, attr.getF32Value());
 47         },
 48         .f64 => {
 49             const value = constant.getFloatValue() orelse return error.CodeGenFailed;
 50             try writer.out.writeByte(0x44);
 51             try wasm.binary.writeF64(writer.out, value);
 52         },
 53         .f16, .bf16 => return error.CodeGenFailed,
 54     }
 55     try writer.writeLocalSet(try writer.localFor(result));
 56 }
 57 
 58 pub fn writeBinary(
 59     comptime Out: type,
 60     writer: *instruction.Writer(Out),
 61     operation: *ir.Operation,
 62     kind: arithmetic.opcode.BinaryKind,
 63 ) emission.Error!void {
 64     const lhs = operation.getOperand(0) orelse return error.CodeGenFailed;
 65     const rhs = operation.getOperand(1) orelse return error.CodeGenFailed;
 66     const result = operation.getResult(0) orelse return error.CodeGenFailed;
 67     const scalar = emission.types.scalarKindForType(result.type) orelse
 68         return error.CodeGenFailed;
 69     try arithmetic.opcode.requireNoSubwordArithmetic(scalar);
 70     try writer.writeValue(lhs);
 71     try writer.writeValue(rhs);
 72     try writer.out.writeByte(try arithmetic.opcode.binary(kind, scalar));
 73     try writer.writeLocalSet(try writer.localFor(result));
 74 }
 75 
 76 pub fn writeCmp(
 77     comptime Out: type,
 78     writer: *instruction.Writer(Out),
 79     operation: *ir.Operation,
 80 ) emission.Error!void {
 81     const cmp = ArithDialect.CmpOp{ .op = operation };
 82     const predicate = cmp.getPredicate() orelse return error.CodeGenFailed;
 83     const lhs = operation.getOperand(0) orelse return error.CodeGenFailed;
 84     const rhs = operation.getOperand(1) orelse return error.CodeGenFailed;
 85     const scalar = emission.types.scalarKindForType(lhs.type) orelse
 86         return error.CodeGenFailed;
 87     try arithmetic.opcode.requireNoSubwordArithmetic(scalar);
 88     try writer.writeValue(lhs);
 89     try writer.writeValue(rhs);
 90     try writer.out.writeByte(try arithmetic.opcode.comparison(predicate, scalar));
 91     try writer.writeLocalSet(try writer.localFor(cmp.getResult()));
 92 }
 93 
 94 pub fn writeSelect(
 95     comptime Out: type,
 96     writer: *instruction.Writer(Out),
 97     operation: *ir.Operation,
 98 ) emission.Error!void {
 99     const condition = operation.getOperand(0) orelse return error.CodeGenFailed;
100     const when_true = operation.getOperand(1) orelse return error.CodeGenFailed;
101     const when_false = operation.getOperand(2) orelse return error.CodeGenFailed;
102     const result = operation.getResult(0) orelse return error.CodeGenFailed;
103     try writer.writeValue(when_true);
104     try writer.writeValue(when_false);
105     try writer.writeValue(condition);
106     try writer.out.writeByte(0x1b);
107     try writer.writeLocalSet(try writer.localFor(result));
108 }
109 
110 pub fn writeNeg(
111     comptime Out: type,
112     writer: *instruction.Writer(Out),
113     operation: *ir.Operation,
114 ) emission.Error!void {
115     try writeUnary(Out, writer, operation, .neg);
116 }
117 
118 pub fn writeAbs(
119     comptime Out: type,
120     writer: *instruction.Writer(Out),
121     operation: *ir.Operation,
122 ) emission.Error!void {
123     try writeUnary(Out, writer, operation, .abs);
124 }
125 
126 pub fn writeSqrt(
127     comptime Out: type,
128     writer: *instruction.Writer(Out),
129     operation: *ir.Operation,
130 ) emission.Error!void {
131     try writeUnary(Out, writer, operation, .sqrt);
132 }
133 
134 fn writeUnary(
135     comptime Out: type,
136     writer: *instruction.Writer(Out),
137     operation: *ir.Operation,
138     kind: UnaryKind,
139 ) emission.Error!void {
140     const operand = operation.getOperand(0) orelse return error.CodeGenFailed;
141     const result = operation.getResult(0) orelse return error.CodeGenFailed;
142     const scalar = emission.types.scalarKindForType(result.type) orelse
143         return error.CodeGenFailed;
144     try arithmetic.opcode.requireNoSubwordArithmetic(scalar);
145     switch (kind) {
146         .neg => switch (scalar) {
147             .i32, .u32, .index, .bool => {
148                 try writer.out.writeByte(0x41);
149                 try wasm.binary.writeSleb(writer.out, 0);
150                 try writer.writeValue(operand);
151                 try writer.out.writeByte(0x6b);
152             },
153             .i64, .u64 => {
154                 try writer.out.writeByte(0x42);
155                 try wasm.binary.writeSleb(writer.out, 0);
156                 try writer.writeValue(operand);
157                 try writer.out.writeByte(0x7d);
158             },
159             .f32 => {
160                 try writer.writeValue(operand);
161                 try writer.out.writeByte(0x8c);
162             },
163             .f64 => {
164                 try writer.writeValue(operand);
165                 try writer.out.writeByte(0x9a);
166             },
167             else => return error.CodeGenFailed,
168         },
169         .abs => {
170             try writer.writeValue(operand);
171             try writer.out.writeByte(switch (scalar) {
172                 .f32 => 0x8b,
173                 .f64 => 0x99,
174                 else => return error.CodeGenFailed,
175             });
176         },
177         .sqrt => {
178             try writer.writeValue(operand);
179             try writer.out.writeByte(switch (scalar) {
180                 .f32 => 0x91,
181                 .f64 => 0x9f,
182                 else => return error.CodeGenFailed,
183             });
184         },
185     }
186     try writer.writeLocalSet(try writer.localFor(result));
187 }