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 }