1use super::*;
2use crate::emit::{SsaVal, EmitError};
3use tidepool_repr::PrimOpKind;
4use cranelift_codegen::ir::{types, InstBuilder, MemFlags, Value, condcodes::IntCC, condcodes::FloatCC};
5use cranelift_frontend::FunctionBuilder;
6
7pub fn emit_primop(
9 builder: &mut FunctionBuilder,
10 op: &PrimOpKind,
11 args: &[SsaVal],
12) -> Result<SsaVal, EmitError> {
13 match op {
14 PrimOpKind::IntAdd => {
16 check_arity(op, 2, args.len())?;
17 let a = unbox_int(builder, args[0]);
18 let b = unbox_int(builder, args[1]);
19 Ok(SsaVal::Raw(builder.ins().iadd(a, b), LIT_TAG_INT))
20 }
21 PrimOpKind::IntSub => {
22 check_arity(op, 2, args.len())?;
23 let a = unbox_int(builder, args[0]);
24 let b = unbox_int(builder, args[1]);
25 Ok(SsaVal::Raw(builder.ins().isub(a, b), LIT_TAG_INT))
26 }
27 PrimOpKind::IntMul => {
28 check_arity(op, 2, args.len())?;
29 let a = unbox_int(builder, args[0]);
30 let b = unbox_int(builder, args[1]);
31 Ok(SsaVal::Raw(builder.ins().imul(a, b), LIT_TAG_INT))
32 }
33 PrimOpKind::IntNegate => {
34 check_arity(op, 1, args.len())?;
35 let a = unbox_int(builder, args[0]);
36 Ok(SsaVal::Raw(builder.ins().ineg(a), LIT_TAG_INT))
37 }
38
39 PrimOpKind::IntEq => emit_int_compare(builder, op, IntCC::Equal, args, LIT_TAG_INT),
41 PrimOpKind::IntNe => emit_int_compare(builder, op, IntCC::NotEqual, args, LIT_TAG_INT),
42 PrimOpKind::IntLt => emit_int_compare(builder, op, IntCC::SignedLessThan, args, LIT_TAG_INT),
43 PrimOpKind::IntLe => emit_int_compare(builder, op, IntCC::SignedLessThanOrEqual, args, LIT_TAG_INT),
44 PrimOpKind::IntGt => emit_int_compare(builder, op, IntCC::SignedGreaterThan, args, LIT_TAG_INT),
45 PrimOpKind::IntGe => emit_int_compare(builder, op, IntCC::SignedGreaterThanOrEqual, args, LIT_TAG_INT),
46
47 PrimOpKind::WordAdd => {
49 check_arity(op, 2, args.len())?;
50 let a = unbox_int(builder, args[0]);
51 let b = unbox_int(builder, args[1]);
52 Ok(SsaVal::Raw(builder.ins().iadd(a, b), LIT_TAG_WORD))
53 }
54 PrimOpKind::WordSub => {
55 check_arity(op, 2, args.len())?;
56 let a = unbox_int(builder, args[0]);
57 let b = unbox_int(builder, args[1]);
58 Ok(SsaVal::Raw(builder.ins().isub(a, b), LIT_TAG_WORD))
59 }
60 PrimOpKind::WordMul => {
61 check_arity(op, 2, args.len())?;
62 let a = unbox_int(builder, args[0]);
63 let b = unbox_int(builder, args[1]);
64 Ok(SsaVal::Raw(builder.ins().imul(a, b), LIT_TAG_WORD))
65 }
66
67 PrimOpKind::WordEq => emit_int_compare(builder, op, IntCC::Equal, args, LIT_TAG_INT),
69 PrimOpKind::WordNe => emit_int_compare(builder, op, IntCC::NotEqual, args, LIT_TAG_INT),
70 PrimOpKind::WordLt => emit_int_compare(builder, op, IntCC::UnsignedLessThan, args, LIT_TAG_INT),
71 PrimOpKind::WordLe => emit_int_compare(builder, op, IntCC::UnsignedLessThanOrEqual, args, LIT_TAG_INT),
72 PrimOpKind::WordGt => emit_int_compare(builder, op, IntCC::UnsignedGreaterThan, args, LIT_TAG_INT),
73 PrimOpKind::WordGe => emit_int_compare(builder, op, IntCC::UnsignedGreaterThanOrEqual, args, LIT_TAG_INT),
74
75 PrimOpKind::DoubleAdd => {
77 check_arity(op, 2, args.len())?;
78 let a = unbox_double(builder, args[0]);
79 let b = unbox_double(builder, args[1]);
80 Ok(SsaVal::Raw(builder.ins().fadd(a, b), LIT_TAG_DOUBLE))
81 }
82 PrimOpKind::DoubleSub => {
83 check_arity(op, 2, args.len())?;
84 let a = unbox_double(builder, args[0]);
85 let b = unbox_double(builder, args[1]);
86 Ok(SsaVal::Raw(builder.ins().fsub(a, b), LIT_TAG_DOUBLE))
87 }
88 PrimOpKind::DoubleMul => {
89 check_arity(op, 2, args.len())?;
90 let a = unbox_double(builder, args[0]);
91 let b = unbox_double(builder, args[1]);
92 Ok(SsaVal::Raw(builder.ins().fmul(a, b), LIT_TAG_DOUBLE))
93 }
94 PrimOpKind::DoubleDiv => {
95 check_arity(op, 2, args.len())?;
96 let a = unbox_double(builder, args[0]);
97 let b = unbox_double(builder, args[1]);
98 Ok(SsaVal::Raw(builder.ins().fdiv(a, b), LIT_TAG_DOUBLE))
99 }
100
101 PrimOpKind::DoubleEq => emit_float_compare(builder, op, FloatCC::Equal, args, LIT_TAG_INT),
103 PrimOpKind::DoubleNe => emit_float_compare(builder, op, FloatCC::NotEqual, args, LIT_TAG_INT),
104 PrimOpKind::DoubleLt => emit_float_compare(builder, op, FloatCC::LessThan, args, LIT_TAG_INT),
105 PrimOpKind::DoubleLe => emit_float_compare(builder, op, FloatCC::LessThanOrEqual, args, LIT_TAG_INT),
106 PrimOpKind::DoubleGt => emit_float_compare(builder, op, FloatCC::GreaterThan, args, LIT_TAG_INT),
107 PrimOpKind::DoubleGe => emit_float_compare(builder, op, FloatCC::GreaterThanOrEqual, args, LIT_TAG_INT),
108
109 PrimOpKind::CharEq => emit_int_compare(builder, op, IntCC::Equal, args, LIT_TAG_INT),
111 PrimOpKind::CharNe => emit_int_compare(builder, op, IntCC::NotEqual, args, LIT_TAG_INT),
112 PrimOpKind::CharLt => emit_int_compare(builder, op, IntCC::UnsignedLessThan, args, LIT_TAG_INT),
113 PrimOpKind::CharLe => emit_int_compare(builder, op, IntCC::UnsignedLessThanOrEqual, args, LIT_TAG_INT),
114 PrimOpKind::CharGt => emit_int_compare(builder, op, IntCC::UnsignedGreaterThan, args, LIT_TAG_INT),
115 PrimOpKind::CharGe => emit_int_compare(builder, op, IntCC::UnsignedGreaterThanOrEqual, args, LIT_TAG_INT),
116
117 PrimOpKind::DataToTag => {
119 check_arity(op, 1, args.len())?;
120 let obj = args[0].value(); let tag = builder.ins().load(types::I64, MemFlags::trusted(), obj, CON_TAG_OFFSET);
123 Ok(SsaVal::Raw(tag, LIT_TAG_INT))
124 }
125 PrimOpKind::IntQuot => {
126 check_arity(op, 2, args.len())?;
127 let a = unbox_int(builder, args[0]);
128 let b = unbox_int(builder, args[1]);
129 Ok(SsaVal::Raw(builder.ins().sdiv(a, b), LIT_TAG_INT))
130 }
131 PrimOpKind::IntRem => {
132 check_arity(op, 2, args.len())?;
133 let a = unbox_int(builder, args[0]);
134 let b = unbox_int(builder, args[1]);
135 Ok(SsaVal::Raw(builder.ins().srem(a, b), LIT_TAG_INT))
136 }
137 PrimOpKind::Chr => {
138 check_arity(op, 1, args.len())?;
139 let v = unbox_int(builder, args[0]);
140 Ok(SsaVal::Raw(v, LIT_TAG_CHAR))
141 }
142 PrimOpKind::Ord => {
143 check_arity(op, 1, args.len())?;
144 let v = unbox_int(builder, args[0]);
145 Ok(SsaVal::Raw(v, LIT_TAG_INT))
146 }
147 PrimOpKind::TagToEnum | PrimOpKind::IndexArray | PrimOpKind::SeqOp => {
148 Err(EmitError::NotYetImplemented(format!("{:?}", op)))
149 }
150 }
151}
152
153fn check_arity(op: &PrimOpKind, expected: usize, got: usize) -> Result<(), EmitError> {
154 if expected != got {
155 Err(EmitError::InvalidArity(*op, expected, got))
156 } else {
157 Ok(())
158 }
159}
160
161fn emit_int_compare(
162 builder: &mut FunctionBuilder,
163 op: &PrimOpKind,
164 cc: IntCC,
165 args: &[SsaVal],
166 tag: i64,
167) -> Result<SsaVal, EmitError> {
168 check_arity(op, 2, args.len())?;
169 let a = unbox_int(builder, args[0]);
170 let b = unbox_int(builder, args[1]);
171 let cmp = builder.ins().icmp(cc, a, b);
172 Ok(SsaVal::Raw(builder.ins().uextend(types::I64, cmp), tag))
173}
174
175fn emit_float_compare(
176 builder: &mut FunctionBuilder,
177 op: &PrimOpKind,
178 cc: FloatCC,
179 args: &[SsaVal],
180 tag: i64,
181) -> Result<SsaVal, EmitError> {
182 check_arity(op, 2, args.len())?;
183 let a = unbox_double(builder, args[0]);
184 let b = unbox_double(builder, args[1]);
185 let cmp = builder.ins().fcmp(cc, a, b);
186 Ok(SsaVal::Raw(builder.ins().uextend(types::I64, cmp), tag))
187}
188
189pub fn unbox_int(builder: &mut FunctionBuilder, val: SsaVal) -> Value {
190 match val {
191 SsaVal::Raw(v, _) => v,
192 SsaVal::HeapPtr(v) => builder.ins().load(types::I64, MemFlags::trusted(), v, LIT_VALUE_OFFSET),
193 }
194}
195
196pub fn unbox_double(builder: &mut FunctionBuilder, val: SsaVal) -> Value {
197 match val {
198 SsaVal::Raw(v, _) => v,
199 SsaVal::HeapPtr(v) => builder.ins().load(types::F64, MemFlags::trusted(), v, LIT_VALUE_OFFSET),
200 }
201}