mirror of
https://github.com/elwrcl/intel-hard-graphics.git
synced 2026-11-01 19:28:02 +03:00
init
This commit is contained in:
12511 files changed
+6156641
No files matched your search
@@ -0,0 +1,168 @@
|
||||
use bitview::{BitMutViewable, BitViewable, SetField};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::ir::{Instr, InstrDeps, Label, Op, OpNop, Shader};
|
||||
|
||||
pub fn instr_latency(_sm: u8, op: &Op, _dst_idx: usize) -> u32 {
|
||||
if op.is_fp64() {
|
||||
return 20;
|
||||
}
|
||||
|
||||
match op {
|
||||
Op::Ipa(_) => 15,
|
||||
Op::Ld(_) => 24,
|
||||
Op::ALd(_) => 24,
|
||||
Op::IMul(_) => 15, // This does not apply to imad, right? right???
|
||||
Op::ISetP(_) => 13,
|
||||
Op::PSetP(_) => 13,
|
||||
Op::IAdd2(o) if !o.carry_out.is_none() => 13,
|
||||
Op::Tex(_)
|
||||
| Op::Tld(_)
|
||||
| Op::Tld4(_)
|
||||
| Op::Tmml(_)
|
||||
| Op::Txd(_)
|
||||
| Op::Txq(_) => 17,
|
||||
_ => 9,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn latency_upper_bound() -> u32 {
|
||||
24
|
||||
}
|
||||
|
||||
pub fn instr_exec_latency(sm: u8, op: &Op) -> u32 {
|
||||
let is_kepler_a = sm == 30;
|
||||
match op {
|
||||
Op::Tex(_)
|
||||
| Op::Tld(_)
|
||||
| Op::Tld4(_)
|
||||
| Op::Tmml(_)
|
||||
| Op::Txd(_)
|
||||
| Op::Txq(_) => 17,
|
||||
Op::MemBar(_) => 16,
|
||||
Op::Cont(_) | Op::Brk(_) if is_kepler_a => 5,
|
||||
Op::Exit(_) => 15,
|
||||
_ => 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn calc_instr_sched(prev_op: Option<&Op>, op: &Op, deps: &InstrDeps) -> u8 {
|
||||
// Kepler is the first generation to lift scoreboarding from the
|
||||
// hardware into the compiler. For each instruction we encode
|
||||
// the delay but not all the other information necessary for newer
|
||||
// architectures.
|
||||
// The hardware still checks for data-hazard and, if present, it
|
||||
// will delay the instruction by 32 cycles.
|
||||
match op {
|
||||
Op::TexDepBar(_) => 0xc2,
|
||||
Op::Sync(_) => 0x00, // Wait 16 cycles
|
||||
_ => {
|
||||
// TODO: when we support dual-issue this should check for
|
||||
// both previous ops
|
||||
let base = match prev_op {
|
||||
Some(Op::ASt(_)) => 0x40,
|
||||
_ => 0x20,
|
||||
};
|
||||
|
||||
let delay = deps.delay;
|
||||
debug_assert!(delay >= 1 && delay <= 32);
|
||||
base | (delay - 1)
|
||||
}
|
||||
}
|
||||
|
||||
// 0x00: wait for 16 cycles
|
||||
// 0x04: dual-issue with next instruction
|
||||
// 0xc2: if TEXBAR
|
||||
// 0x20 | 0x40: suspend for N+1 cycles (N = bitmask 0x1f)
|
||||
// 0x40 only if prev_op is attribute store
|
||||
// Unsure:
|
||||
// 0x80: global memory bit
|
||||
//
|
||||
// TODO:
|
||||
// - Dual issue (0x04)
|
||||
// - Functional Unit tracking
|
||||
}
|
||||
|
||||
pub trait KeplerInstructionEncoder {
|
||||
/// Encode the instruction and push it into the "encoded" vec
|
||||
fn encode_instr(
|
||||
&self,
|
||||
instr: &Instr,
|
||||
labels: &FxHashMap<Label, usize>,
|
||||
encoded: &mut Vec<u32>,
|
||||
);
|
||||
|
||||
/// Prepare the scheduling instruction opcode-field and return a
|
||||
/// subset where the actual scheduling information will be written
|
||||
fn prepare_sched_instr<'a>(
|
||||
&self,
|
||||
sched_instr: &'a mut [u32; 2],
|
||||
) -> impl BitMutViewable + 'a;
|
||||
}
|
||||
|
||||
/// Helper function that encodes shaders for both KeplerA and KeplerB.
|
||||
/// Difference in the encoders are handled by KeplerInstructionEncoder.
|
||||
pub fn encode_kepler_shader<E>(encoder: &E, s: &Shader<'_>) -> Vec<u32>
|
||||
where
|
||||
E: KeplerInstructionEncoder,
|
||||
{
|
||||
const INSTR_LEN_BYTES: usize = 8;
|
||||
assert!(s.functions.len() == 1);
|
||||
let func = &s.functions[0];
|
||||
|
||||
// --- Compute label addresses ---
|
||||
// We need a schedule instruction every 7 instructions, these don't
|
||||
// define jump boundaries so we can have multible blocks in the same
|
||||
// 7-instr group.
|
||||
let mut ip = 0_usize;
|
||||
let mut labels = FxHashMap::default();
|
||||
for b in &func.blocks {
|
||||
let num_sched = (ip / 7) + 1;
|
||||
labels.insert(b.label, (ip + num_sched) * INSTR_LEN_BYTES);
|
||||
ip += b.instrs.len();
|
||||
}
|
||||
|
||||
// --- Real encoding ---
|
||||
// Create an instruction iterator and iterate it in chunks of 7.
|
||||
// fill the last chunk with a nop (it should never be executed).
|
||||
let mut instr_iter =
|
||||
func.blocks.iter().flat_map(|b| b.instrs.iter()).peekable();
|
||||
let mut filling_instr = Instr {
|
||||
pred: true.into(),
|
||||
op: Op::Nop(OpNop { label: None }),
|
||||
deps: InstrDeps::new(),
|
||||
};
|
||||
filling_instr.deps.set_delay(1);
|
||||
let mut sched_chunk_gen = || {
|
||||
if instr_iter.peek().is_none() {
|
||||
return None;
|
||||
}
|
||||
Some([0; 7].map(|_| instr_iter.next().unwrap_or(&filling_instr)))
|
||||
};
|
||||
|
||||
let mut encoded = Vec::new();
|
||||
let mut prev_op = None;
|
||||
while let Some(sched_chunk) = sched_chunk_gen() {
|
||||
let sched_i = encoded.len();
|
||||
|
||||
let mut sched_instr = [0u32; 2];
|
||||
encoded.extend(&sched_instr[..]); // Push now, will edit later
|
||||
let mut bv = encoder.prepare_sched_instr(&mut sched_instr);
|
||||
// There should be 8 bits for each instr in a scheduling block
|
||||
debug_assert!(bv.bits() == 8 * 7);
|
||||
|
||||
for (i, instr) in sched_chunk.iter().enumerate() {
|
||||
encoder.encode_instr(instr, &labels, &mut encoded);
|
||||
|
||||
let sched = calc_instr_sched(prev_op, &instr.op, &instr.deps);
|
||||
bv.set_field(i * 8..(i + 1) * 8, sched);
|
||||
prev_op = Some(&instr.op);
|
||||
}
|
||||
|
||||
drop(bv);
|
||||
encoded[sched_i] = sched_instr[0];
|
||||
encoded[sched_i + 1] = sched_instr[1];
|
||||
}
|
||||
|
||||
encoded
|
||||
}
|
||||
Reference in new issue
Block a user