Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Commit a1517e4

Browse files
committed
[meta] Recipes and encodings descriptions for RiscV;
1 parent 5fcd123 commit a1517e4

File tree

3 files changed

+662
-5
lines changed

3 files changed

+662
-5
lines changed
Lines changed: 383 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,383 @@
1+
use crate::cdsl::ast::{Apply, Expr, Literal, VarPool};
2+
use crate::cdsl::encodings::{Encoding, EncodingBuilder};
3+
use crate::cdsl::instructions::{
4+
BoundInstruction, InstSpec, InstructionPredicateNode, InstructionPredicateRegistry,
5+
};
6+
use crate::cdsl::recipes::{EncodingRecipeNumber, Recipes};
7+
use crate::cdsl::settings::SettingGroup;
8+
9+
use crate::shared::types::Bool::B1;
10+
use crate::shared::types::Int::{I32, I64};
11+
use crate::shared::Definitions as SharedDefinitions;
12+
13+
use super::recipes::RecipeGroup;
14+
15+
fn enc(inst: impl Into<InstSpec>, recipe: EncodingRecipeNumber, bits: u16) -> EncodingBuilder {
16+
EncodingBuilder::new(inst.into(), recipe, bits)
17+
}
18+
19+
pub struct PerCpuModeEncodings<'defs> {
20+
pub inst_pred_reg: InstructionPredicateRegistry,
21+
pub enc32: Vec<Encoding>,
22+
pub enc64: Vec<Encoding>,
23+
recipes: &'defs Recipes,
24+
}
25+
26+
impl<'defs> PerCpuModeEncodings<'defs> {
27+
fn new(recipes: &'defs Recipes) -> Self {
28+
Self {
29+
inst_pred_reg: InstructionPredicateRegistry::new(),
30+
enc32: Vec::new(),
31+
enc64: Vec::new(),
32+
recipes,
33+
}
34+
}
35+
fn add32(&mut self, encoding: EncodingBuilder) {
36+
self.enc32
37+
.push(encoding.build(self.recipes, &mut self.inst_pred_reg));
38+
}
39+
fn add64(&mut self, encoding: EncodingBuilder) {
40+
self.enc64
41+
.push(encoding.build(self.recipes, &mut self.inst_pred_reg));
42+
}
43+
}
44+
45+
// The low 7 bits of a RISC-V instruction is the base opcode. All 32-bit instructions have 11 as
46+
// the two low bits, with bits 6:2 determining the base opcode.
47+
//
48+
// Encbits for the 32-bit recipes are opcode[6:2] | (funct3 << 5) | ...
49+
// The functions below encode the encbits.
50+
51+
fn load_bits(funct3: u16) -> u16 {
52+
assert!(funct3 <= 0b111);
53+
0b00000 | (funct3 << 5)
54+
}
55+
56+
fn store_bits(funct3: u16) -> u16 {
57+
assert!(funct3 <= 0b111);
58+
0b01000 | (funct3 << 5)
59+
}
60+
61+
fn branch_bits(funct3: u16) -> u16 {
62+
assert!(funct3 <= 0b111);
63+
0b11000 | (funct3 << 5)
64+
}
65+
66+
fn jalr_bits() -> u16 {
67+
// This was previously accepting an argument funct3 of 3 bits and used the following formula:
68+
//0b11001 | (funct3 << 5)
69+
0b11001
70+
}
71+
72+
fn jal_bits() -> u16 {
73+
0b11011
74+
}
75+
76+
fn opimm_bits(funct3: u16, funct7: u16) -> u16 {
77+
assert!(funct3 <= 0b111);
78+
0b00100 | (funct3 << 5) | (funct7 << 8)
79+
}
80+
81+
fn opimm32_bits(funct3: u16, funct7: u16) -> u16 {
82+
assert!(funct3 <= 0b111);
83+
0b00110 | (funct3 << 5) | (funct7 << 8)
84+
}
85+
86+
fn op_bits(funct3: u16, funct7: u16) -> u16 {
87+
assert!(funct3 <= 0b111);
88+
assert!(funct7 <= 0b1111111);
89+
0b01100 | (funct3 << 5) | (funct7 << 8)
90+
}
91+
92+
fn op32_bits(funct3: u16, funct7: u16) -> u16 {
93+
assert!(funct3 <= 0b111);
94+
assert!(funct7 <= 0b1111111);
95+
0b01110 | (funct3 << 5) | (funct7 << 8)
96+
}
97+
98+
fn lui_bits() -> u16 {
99+
0b01101
100+
}
101+
102+
pub fn define<'defs>(
103+
shared_defs: &'defs SharedDefinitions,
104+
isa_settings: &SettingGroup,
105+
recipes: &'defs RecipeGroup,
106+
) -> PerCpuModeEncodings<'defs> {
107+
// Instructions shorthands.
108+
let shared = &shared_defs.instructions;
109+
110+
let band = shared.by_name("band");
111+
let band_imm = shared.by_name("band_imm");
112+
let bor = shared.by_name("bor");
113+
let bor_imm = shared.by_name("bor_imm");
114+
let br_icmp = shared.by_name("br_icmp");
115+
let brz = shared.by_name("brz");
116+
let brnz = shared.by_name("brnz");
117+
let bxor = shared.by_name("bxor");
118+
let bxor_imm = shared.by_name("bxor_imm");
119+
let call = shared.by_name("call");
120+
let call_indirect = shared.by_name("call_indirect");
121+
let copy = shared.by_name("copy");
122+
let fill = shared.by_name("fill");
123+
let iadd = shared.by_name("iadd");
124+
let iadd_imm = shared.by_name("iadd_imm");
125+
let iconst = shared.by_name("iconst");
126+
let icmp = shared.by_name("icmp");
127+
let icmp_imm = shared.by_name("icmp_imm");
128+
let imul = shared.by_name("imul");
129+
let ishl = shared.by_name("ishl");
130+
let ishl_imm = shared.by_name("ishl_imm");
131+
let isub = shared.by_name("isub");
132+
let jump = shared.by_name("jump");
133+
let regmove = shared.by_name("regmove");
134+
let spill = shared.by_name("spill");
135+
let sshr = shared.by_name("sshr");
136+
let sshr_imm = shared.by_name("sshr_imm");
137+
let ushr = shared.by_name("ushr");
138+
let ushr_imm = shared.by_name("ushr_imm");
139+
let return_ = shared.by_name("return");
140+
141+
// Recipes shorthands, prefixed with r_.
142+
let r_icall = recipes.by_name("Icall");
143+
let r_icopy = recipes.by_name("Icopy");
144+
let r_ii = recipes.by_name("Ii");
145+
let r_iicmp = recipes.by_name("Iicmp");
146+
let r_iret = recipes.by_name("Iret");
147+
let r_irmov = recipes.by_name("Irmov");
148+
let r_iz = recipes.by_name("Iz");
149+
let r_gp_sp = recipes.by_name("GPsp");
150+
let r_gp_fi = recipes.by_name("GPfi");
151+
let r_r = recipes.by_name("R");
152+
let r_ricmp = recipes.by_name("Ricmp");
153+
let r_rshamt = recipes.by_name("Rshamt");
154+
let r_sb = recipes.by_name("SB");
155+
let r_sb_zero = recipes.by_name("SBzero");
156+
let r_u = recipes.by_name("U");
157+
let r_uj = recipes.by_name("UJ");
158+
let r_uj_call = recipes.by_name("UJcall");
159+
160+
// Predicates shorthands.
161+
let use_m = isa_settings.predicate_by_name("use_m");
162+
163+
// Definitions.
164+
let mut e = PerCpuModeEncodings::new(&recipes.recipes);
165+
166+
// Basic arithmetic binary instructions are encoded in an R-type instruction.
167+
for &(inst, inst_imm, f3, f7) in &[
168+
(iadd, Some(iadd_imm), 0b000, 0b0000000),
169+
(isub, None, 0b000, 0b0100000),
170+
(bxor, Some(bxor_imm), 0b100, 0b0000000),
171+
(bor, Some(bor_imm), 0b110, 0b0000000),
172+
(band, Some(band_imm), 0b111, 0b0000000),
173+
] {
174+
e.add32(enc(inst.bind(I32), r_r, op_bits(f3, f7)));
175+
e.add64(enc(inst.bind(I64), r_r, op_bits(f3, f7)));
176+
177+
// Immediate versions for add/xor/or/and.
178+
if let Some(inst_imm) = inst_imm {
179+
e.add32(enc(inst_imm.bind(I32), r_ii, opimm_bits(f3, 0)));
180+
e.add64(enc(inst_imm.bind(I64), r_ii, opimm_bits(f3, 0)));
181+
}
182+
}
183+
184+
// 32-bit ops in RV64.
185+
e.add64(enc(iadd.bind(I32), r_r, op32_bits(0b000, 0b0000000)));
186+
e.add64(enc(isub.bind(I32), r_r, op32_bits(0b000, 0b0100000)));
187+
// There are no andiw/oriw/xoriw variations.
188+
e.add64(enc(iadd_imm.bind(I32), r_ii, opimm32_bits(0b000, 0)));
189+
190+
// Use iadd_imm with %x0 to materialize constants.
191+
e.add32(enc(iconst.bind(I32), r_iz, opimm_bits(0b0, 0)));
192+
e.add64(enc(iconst.bind(I32), r_iz, opimm_bits(0b0, 0)));
193+
e.add64(enc(iconst.bind(I64), r_iz, opimm_bits(0b0, 0)));
194+
195+
// Dynamic shifts have the same masking semantics as the clif base instructions.
196+
for &(inst, inst_imm, f3, f7) in &[
197+
(ishl, ishl_imm, 0b1, 0b0),
198+
(ushr, ushr_imm, 0b101, 0b0),
199+
(sshr, sshr_imm, 0b101, 0b100000),
200+
] {
201+
e.add32(enc(inst.bind(I32).bind(I32), r_r, op_bits(f3, f7)));
202+
e.add64(enc(inst.bind(I64).bind(I64), r_r, op_bits(f3, f7)));
203+
e.add64(enc(inst.bind(I32).bind(I32), r_r, op32_bits(f3, f7)));
204+
// Allow i32 shift amounts in 64-bit shifts.
205+
e.add64(enc(inst.bind(I64).bind(I32), r_r, op_bits(f3, f7)));
206+
e.add64(enc(inst.bind(I32).bind(I64), r_r, op32_bits(f3, f7)));
207+
208+
// Immediate shifts.
209+
e.add32(enc(inst_imm.bind(I32), r_rshamt, opimm_bits(f3, f7)));
210+
e.add64(enc(inst_imm.bind(I64), r_rshamt, opimm_bits(f3, f7)));
211+
e.add64(enc(inst_imm.bind(I32), r_rshamt, opimm32_bits(f3, f7)));
212+
}
213+
214+
// Signed and unsigned integer 'less than'. There are no 'w' variants for comparing 32-bit
215+
// numbers in RV64.
216+
{
217+
let mut var_pool = VarPool::new();
218+
219+
// Helper that creates an instruction predicate for an instruction in the icmp family.
220+
let mut icmp_instp = |bound_inst: &BoundInstruction,
221+
intcc_field: &'static str|
222+
-> InstructionPredicateNode {
223+
let x = var_pool.create("x");
224+
let y = var_pool.create("y");
225+
let cc =
226+
Literal::enumerator_for(shared_defs.operand_kinds.by_name("intcc"), intcc_field);
227+
Apply::new(
228+
bound_inst.clone().into(),
229+
vec![Expr::Literal(cc), Expr::Var(x), Expr::Var(y)],
230+
)
231+
.inst_predicate(&shared_defs.format_registry, &var_pool)
232+
.unwrap()
233+
};
234+
235+
let icmp_i32 = icmp.bind(I32);
236+
let icmp_i64 = icmp.bind(I64);
237+
e.add32(
238+
enc(icmp_i32.clone(), r_ricmp, op_bits(0b010, 0b0000000))
239+
.inst_predicate(icmp_instp(&icmp_i32, "slt")),
240+
);
241+
e.add64(
242+
enc(icmp_i64.clone(), r_ricmp, op_bits(0b010, 0b0000000))
243+
.inst_predicate(icmp_instp(&icmp_i64, "slt")),
244+
);
245+
246+
e.add32(
247+
enc(icmp_i32.clone(), r_ricmp, op_bits(0b011, 0b0000000))
248+
.inst_predicate(icmp_instp(&icmp_i32, "ult")),
249+
);
250+
e.add64(
251+
enc(icmp_i64.clone(), r_ricmp, op_bits(0b011, 0b0000000))
252+
.inst_predicate(icmp_instp(&icmp_i64, "ult")),
253+
);
254+
255+
// Immediate variants.
256+
let icmp_i32 = icmp_imm.bind(I32);
257+
let icmp_i64 = icmp_imm.bind(I64);
258+
e.add32(
259+
enc(icmp_i32.clone(), r_iicmp, opimm_bits(0b010, 0))
260+
.inst_predicate(icmp_instp(&icmp_i32, "slt")),
261+
);
262+
e.add64(
263+
enc(icmp_i64.clone(), r_iicmp, opimm_bits(0b010, 0))
264+
.inst_predicate(icmp_instp(&icmp_i64, "slt")),
265+
);
266+
267+
e.add32(
268+
enc(icmp_i32.clone(), r_iicmp, opimm_bits(0b011, 0))
269+
.inst_predicate(icmp_instp(&icmp_i32, "ult")),
270+
);
271+
e.add64(
272+
enc(icmp_i64.clone(), r_iicmp, opimm_bits(0b011, 0))
273+
.inst_predicate(icmp_instp(&icmp_i64, "ult")),
274+
);
275+
}
276+
277+
// Integer constants with the low 12 bits clear are materialized by lui.
278+
e.add32(enc(iconst.bind(I32), r_u, lui_bits()));
279+
e.add64(enc(iconst.bind(I32), r_u, lui_bits()));
280+
e.add64(enc(iconst.bind(I64), r_u, lui_bits()));
281+
282+
// "M" Standard Extension for Integer Multiplication and Division.
283+
// Gated by the `use_m` flag.
284+
e.add32(enc(imul.bind(I32), r_r, op_bits(0b000, 0b00000001)).isa_predicate(use_m));
285+
e.add64(enc(imul.bind(I64), r_r, op_bits(0b000, 0b00000001)).isa_predicate(use_m));
286+
e.add64(enc(imul.bind(I32), r_r, op32_bits(0b000, 0b00000001)).isa_predicate(use_m));
287+
288+
// Control flow.
289+
290+
// Unconditional branches.
291+
e.add32(enc(jump, r_uj, jal_bits()));
292+
e.add64(enc(jump, r_uj, jal_bits()));
293+
e.add32(enc(call, r_uj_call, jal_bits()));
294+
e.add64(enc(call, r_uj_call, jal_bits()));
295+
296+
// Conditional branches.
297+
{
298+
let mut var_pool = VarPool::new();
299+
300+
// Helper that creates an instruction predicate for an instruction in the icmp family.
301+
let mut br_icmp_instp = |bound_inst: &BoundInstruction,
302+
intcc_field: &'static str|
303+
-> InstructionPredicateNode {
304+
let x = var_pool.create("x");
305+
let y = var_pool.create("y");
306+
let dest = var_pool.create("dest");
307+
let args = var_pool.create("args");
308+
let cc =
309+
Literal::enumerator_for(shared_defs.operand_kinds.by_name("intcc"), intcc_field);
310+
Apply::new(
311+
bound_inst.clone().into(),
312+
vec![
313+
Expr::Literal(cc),
314+
Expr::Var(x),
315+
Expr::Var(y),
316+
Expr::Var(dest),
317+
Expr::Var(args),
318+
],
319+
)
320+
.inst_predicate(&shared_defs.format_registry, &var_pool)
321+
.unwrap()
322+
};
323+
324+
let br_icmp_i32 = br_icmp.bind(I32);
325+
let br_icmp_i64 = br_icmp.bind(I64);
326+
for &(cond, f3) in &[
327+
("eq", 0b000),
328+
("ne", 0b001),
329+
("slt", 0b100),
330+
("sge", 0b101),
331+
("ult", 0b110),
332+
("uge", 0b111),
333+
] {
334+
e.add32(
335+
enc(br_icmp_i32.clone(), r_sb, branch_bits(f3))
336+
.inst_predicate(br_icmp_instp(&br_icmp_i32, cond)),
337+
);
338+
e.add64(
339+
enc(br_icmp_i64.clone(), r_sb, branch_bits(f3))
340+
.inst_predicate(br_icmp_instp(&br_icmp_i64, cond)),
341+
);
342+
}
343+
}
344+
345+
for &(inst, f3) in &[(brz, 0b000), (brnz, 0b001)] {
346+
e.add32(enc(inst.bind(I32), r_sb_zero, branch_bits(f3)));
347+
e.add64(enc(inst.bind(I64), r_sb_zero, branch_bits(f3)));
348+
e.add32(enc(inst.bind(B1), r_sb_zero, branch_bits(f3)));
349+
e.add64(enc(inst.bind(B1), r_sb_zero, branch_bits(f3)));
350+
}
351+
352+
// Returns are a special case of jalr_bits using %x1 to hold the return address.
353+
// The return address is provided by a special-purpose `link` return value that
354+
// is added by legalize_signature().
355+
e.add32(enc(return_, r_iret, jalr_bits()));
356+
e.add64(enc(return_, r_iret, jalr_bits()));
357+
e.add32(enc(call_indirect.bind(I32), r_icall, jalr_bits()));
358+
e.add64(enc(call_indirect.bind(I64), r_icall, jalr_bits()));
359+
360+
// Spill and fill.
361+
e.add32(enc(spill.bind(I32), r_gp_sp, store_bits(0b010)));
362+
e.add64(enc(spill.bind(I32), r_gp_sp, store_bits(0b010)));
363+
e.add64(enc(spill.bind(I64), r_gp_sp, store_bits(0b011)));
364+
e.add32(enc(fill.bind(I32), r_gp_fi, load_bits(0b010)));
365+
e.add64(enc(fill.bind(I32), r_gp_fi, load_bits(0b010)));
366+
e.add64(enc(fill.bind(I64), r_gp_fi, load_bits(0b011)));
367+
368+
// Register copies.
369+
e.add32(enc(copy.bind(I32), r_icopy, opimm_bits(0b000, 0)));
370+
e.add64(enc(copy.bind(I64), r_icopy, opimm_bits(0b000, 0)));
371+
e.add64(enc(copy.bind(I32), r_icopy, opimm32_bits(0b000, 0)));
372+
373+
e.add32(enc(regmove.bind(I32), r_irmov, opimm_bits(0b000, 0)));
374+
e.add64(enc(regmove.bind(I64), r_irmov, opimm_bits(0b000, 0)));
375+
e.add64(enc(regmove.bind(I32), r_irmov, opimm32_bits(0b000, 0)));
376+
377+
e.add32(enc(copy.bind(B1), r_icopy, opimm_bits(0b000, 0)));
378+
e.add64(enc(copy.bind(B1), r_icopy, opimm_bits(0b000, 0)));
379+
e.add32(enc(regmove.bind(B1), r_irmov, opimm_bits(0b000, 0)));
380+
e.add64(enc(regmove.bind(B1), r_irmov, opimm_bits(0b000, 0)));
381+
382+
e
383+
}

0 commit comments

Comments
 (0)