use crate::{
analysis::SsaType,
assembly::{Immediate, InstructionEncoder, Operand},
Result,
};
#[allow(clippy::cast_possible_truncation)] pub fn emit_ldc_i4(encoder: &mut InstructionEncoder, v: i32) -> Result<()> {
match v {
-1 => encoder.emit_instruction("ldc.i4.m1", None)?,
0 => encoder.emit_instruction("ldc.i4.0", None)?,
1 => encoder.emit_instruction("ldc.i4.1", None)?,
2 => encoder.emit_instruction("ldc.i4.2", None)?,
3 => encoder.emit_instruction("ldc.i4.3", None)?,
4 => encoder.emit_instruction("ldc.i4.4", None)?,
5 => encoder.emit_instruction("ldc.i4.5", None)?,
6 => encoder.emit_instruction("ldc.i4.6", None)?,
7 => encoder.emit_instruction("ldc.i4.7", None)?,
8 => encoder.emit_instruction("ldc.i4.8", None)?,
x if (-128..=127).contains(&x) => {
encoder.emit_instruction(
"ldc.i4.s",
Some(Operand::Immediate(Immediate::Int8(x as i8))),
)?;
}
x => {
encoder.emit_instruction("ldc.i4", Some(Operand::Immediate(Immediate::Int32(x))))?;
}
}
Ok(())
}
pub fn emit_ldarg(encoder: &mut InstructionEncoder, index: u16) -> Result<()> {
match index {
0 => encoder.emit_instruction("ldarg.0", None)?,
1 => encoder.emit_instruction("ldarg.1", None)?,
2 => encoder.emit_instruction("ldarg.2", None)?,
3 => encoder.emit_instruction("ldarg.3", None)?,
x if x <= 255 => {
#[allow(clippy::cast_possible_truncation)]
let short_idx = x as u8;
encoder.emit_instruction(
"ldarg.s",
Some(Operand::Immediate(Immediate::UInt8(short_idx))),
)?;
}
x => {
#[allow(clippy::cast_possible_wrap)]
let signed_idx = x as i16;
encoder.emit_instruction(
"ldarg",
Some(Operand::Immediate(Immediate::Int16(signed_idx))),
)?;
}
}
Ok(())
}
pub fn emit_ldloc(encoder: &mut InstructionEncoder, index: u16) -> Result<()> {
match index {
0 => encoder.emit_instruction("ldloc.0", None)?,
1 => encoder.emit_instruction("ldloc.1", None)?,
2 => encoder.emit_instruction("ldloc.2", None)?,
3 => encoder.emit_instruction("ldloc.3", None)?,
x if x <= 255 => {
#[allow(clippy::cast_possible_truncation)]
let short_idx = x as u8;
encoder.emit_instruction(
"ldloc.s",
Some(Operand::Immediate(Immediate::UInt8(short_idx))),
)?;
}
x => {
#[allow(clippy::cast_possible_wrap)]
let signed_idx = x as i16;
encoder.emit_instruction(
"ldloc",
Some(Operand::Immediate(Immediate::Int16(signed_idx))),
)?;
}
}
Ok(())
}
pub fn emit_starg(encoder: &mut InstructionEncoder, index: u16) -> Result<()> {
if index <= 255 {
#[allow(clippy::cast_possible_truncation)]
let short_idx = index as u8;
encoder.emit_instruction(
"starg.s",
Some(Operand::Immediate(Immediate::UInt8(short_idx))),
)?;
} else {
#[allow(clippy::cast_possible_wrap)]
let signed_idx = index as i16;
encoder.emit_instruction(
"starg",
Some(Operand::Immediate(Immediate::Int16(signed_idx))),
)?;
}
Ok(())
}
pub fn emit_stloc(encoder: &mut InstructionEncoder, index: u16) -> Result<()> {
match index {
0 => encoder.emit_instruction("stloc.0", None)?,
1 => encoder.emit_instruction("stloc.1", None)?,
2 => encoder.emit_instruction("stloc.2", None)?,
3 => encoder.emit_instruction("stloc.3", None)?,
x if x <= 255 => {
#[allow(clippy::cast_possible_truncation)]
let short_idx = x as u8;
encoder.emit_instruction(
"stloc.s",
Some(Operand::Immediate(Immediate::UInt8(short_idx))),
)?;
}
x => {
#[allow(clippy::cast_possible_wrap)]
let signed_idx = x as i16;
encoder.emit_instruction(
"stloc",
Some(Operand::Immediate(Immediate::Int16(signed_idx))),
)?;
}
}
Ok(())
}
pub fn emit_ldarga(encoder: &mut InstructionEncoder, index: u16) -> Result<()> {
if index <= 255 {
#[allow(clippy::cast_possible_truncation)]
let short_idx = index as u8;
encoder.emit_instruction(
"ldarga.s",
Some(Operand::Immediate(Immediate::UInt8(short_idx))),
)?;
} else {
#[allow(clippy::cast_possible_wrap)]
let signed_idx = index as i16;
encoder.emit_instruction(
"ldarga",
Some(Operand::Immediate(Immediate::Int16(signed_idx))),
)?;
}
Ok(())
}
pub fn emit_ldloca(encoder: &mut InstructionEncoder, index: u16) -> Result<()> {
if index <= 255 {
#[allow(clippy::cast_possible_truncation)]
let short_idx = index as u8;
encoder.emit_instruction(
"ldloca.s",
Some(Operand::Immediate(Immediate::UInt8(short_idx))),
)?;
} else {
#[allow(clippy::cast_possible_wrap)]
let signed_idx = index as i16;
encoder.emit_instruction(
"ldloca",
Some(Operand::Immediate(Immediate::Int16(signed_idx))),
)?;
}
Ok(())
}
pub fn emit_conv(
encoder: &mut InstructionEncoder,
target: &SsaType,
overflow_check: bool,
unsigned: bool,
) -> Result<()> {
let instr = match (target, overflow_check, unsigned) {
(SsaType::I8, false, _) => "conv.i1",
(SsaType::I16, false, _) => "conv.i2",
(SsaType::I32, false, _) => "conv.i4",
(SsaType::I64, false, _) => "conv.i8",
(SsaType::U8, false, _) => "conv.u1",
(SsaType::U16, false, _) => "conv.u2",
(SsaType::U32, false, _) => "conv.u4",
(SsaType::U64, false, _) => "conv.u8",
(SsaType::F32, false, _) => "conv.r4",
(SsaType::F64, false, _) => "conv.r8",
(SsaType::NativeInt, false, _) => "conv.i",
(SsaType::NativeUInt, false, _) => "conv.u",
(SsaType::I8, true, false) => "conv.ovf.i1",
(SsaType::I16, true, false) => "conv.ovf.i2",
(SsaType::I32, true, false) => "conv.ovf.i4",
(SsaType::I64, true, false) => "conv.ovf.i8",
(SsaType::U8, true, false) => "conv.ovf.u1",
(SsaType::U16, true, false) => "conv.ovf.u2",
(SsaType::U32, true, false) => "conv.ovf.u4",
(SsaType::U64, true, false) => "conv.ovf.u8",
(SsaType::NativeInt, true, false) => "conv.ovf.i",
(SsaType::NativeUInt, true, false) => "conv.ovf.u",
(SsaType::I8, true, true) => "conv.ovf.i1.un",
(SsaType::I16, true, true) => "conv.ovf.i2.un",
(SsaType::I32, true, true) => "conv.ovf.i4.un",
(SsaType::I64, true, true) => "conv.ovf.i8.un",
(SsaType::U8, true, true) => "conv.ovf.u1.un",
(SsaType::U16, true, true) => "conv.ovf.u2.un",
(SsaType::U32, true, true) => "conv.ovf.u4.un",
(SsaType::U64, true, true) => "conv.ovf.u8.un",
(SsaType::NativeInt, true, true) => "conv.ovf.i.un",
(SsaType::NativeUInt, true, true) => "conv.ovf.u.un",
(SsaType::F32 | SsaType::F64, _, true) => "conv.r.un",
_ => return Ok(()),
};
encoder.emit_instruction(instr, None)?;
Ok(())
}
pub fn emit_ldelem(encoder: &mut InstructionEncoder, elem_type: &SsaType) -> Result<()> {
let instr = match elem_type {
SsaType::I8 => "ldelem.i1",
SsaType::I16 => "ldelem.i2",
SsaType::I32 => "ldelem.i4",
SsaType::I64 | SsaType::U64 => "ldelem.i8",
SsaType::U8 => "ldelem.u1",
SsaType::U16 => "ldelem.u2",
SsaType::U32 => "ldelem.u4",
SsaType::F32 => "ldelem.r4",
SsaType::F64 => "ldelem.r8",
SsaType::NativeInt | SsaType::NativeUInt => "ldelem.i",
_ => "ldelem.ref", };
encoder.emit_instruction(instr, None)?;
Ok(())
}
pub fn emit_stelem(encoder: &mut InstructionEncoder, elem_type: &SsaType) -> Result<()> {
let instr = match elem_type {
SsaType::I8 | SsaType::U8 => "stelem.i1",
SsaType::I16 | SsaType::U16 => "stelem.i2",
SsaType::I32 | SsaType::U32 => "stelem.i4",
SsaType::I64 | SsaType::U64 => "stelem.i8",
SsaType::F32 => "stelem.r4",
SsaType::F64 => "stelem.r8",
SsaType::NativeInt | SsaType::NativeUInt => "stelem.i",
_ => "stelem.ref", };
encoder.emit_instruction(instr, None)?;
Ok(())
}
pub fn emit_ldind(encoder: &mut InstructionEncoder, value_type: &SsaType) -> Result<()> {
let instr = match value_type {
SsaType::I8 => "ldind.i1",
SsaType::I16 => "ldind.i2",
SsaType::I32 => "ldind.i4",
SsaType::I64 | SsaType::U64 => "ldind.i8",
SsaType::U8 => "ldind.u1",
SsaType::U16 => "ldind.u2",
SsaType::U32 => "ldind.u4",
SsaType::F32 => "ldind.r4",
SsaType::F64 => "ldind.r8",
SsaType::NativeInt | SsaType::NativeUInt => "ldind.i",
_ => "ldind.ref", };
encoder.emit_instruction(instr, None)?;
Ok(())
}
pub fn emit_stind(encoder: &mut InstructionEncoder, value_type: &SsaType) -> Result<()> {
let instr = match value_type {
SsaType::I8 | SsaType::U8 => "stind.i1",
SsaType::I16 | SsaType::U16 => "stind.i2",
SsaType::I32 | SsaType::U32 => "stind.i4",
SsaType::I64 | SsaType::U64 => "stind.i8",
SsaType::F32 => "stind.r4",
SsaType::F64 => "stind.r8",
SsaType::NativeInt | SsaType::NativeUInt => "stind.i",
_ => "stind.ref", };
encoder.emit_instruction(instr, None)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_emit_ldc_i4_special_values() {
let mut encoder = InstructionEncoder::new();
emit_ldc_i4(&mut encoder, -1).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_ldc_i4(&mut encoder, 1).unwrap();
emit_ldc_i4(&mut encoder, 8).unwrap();
emit_ldc_i4(&mut encoder, 42).unwrap();
emit_ldc_i4(&mut encoder, -100).unwrap();
emit_ldc_i4(&mut encoder, 1000).unwrap();
emit_ldc_i4(&mut encoder, -1000).unwrap();
let (bytecode, _, _) = encoder.finalize().unwrap();
assert!(!bytecode.is_empty());
}
#[test]
fn test_emit_ldarg_encoding() {
let mut encoder = InstructionEncoder::new();
emit_ldarg(&mut encoder, 0).unwrap();
emit_ldarg(&mut encoder, 1).unwrap();
emit_ldarg(&mut encoder, 2).unwrap();
emit_ldarg(&mut encoder, 3).unwrap();
emit_ldarg(&mut encoder, 10).unwrap();
emit_ldarg(&mut encoder, 300).unwrap();
let (bytecode, _, _) = encoder.finalize().unwrap();
assert!(!bytecode.is_empty());
}
#[test]
fn test_emit_ldloc_encoding() {
let mut encoder = InstructionEncoder::new();
emit_ldloc(&mut encoder, 0).unwrap();
emit_ldloc(&mut encoder, 3).unwrap();
emit_ldloc(&mut encoder, 100).unwrap();
let (bytecode, _, _) = encoder.finalize().unwrap();
assert!(!bytecode.is_empty());
}
#[test]
fn test_emit_stloc_encoding() {
let mut encoder = InstructionEncoder::new();
emit_ldc_i4(&mut encoder, 1).unwrap();
emit_stloc(&mut encoder, 0).unwrap();
emit_ldc_i4(&mut encoder, 2).unwrap();
emit_stloc(&mut encoder, 3).unwrap();
emit_ldc_i4(&mut encoder, 3).unwrap();
emit_stloc(&mut encoder, 100).unwrap();
let (bytecode, _, _) = encoder.finalize().unwrap();
assert!(!bytecode.is_empty());
}
#[test]
fn test_emit_conv_variants() {
let mut encoder = InstructionEncoder::new();
emit_conv(&mut encoder, &SsaType::I32, false, false).unwrap();
emit_conv(&mut encoder, &SsaType::I64, false, false).unwrap();
emit_conv(&mut encoder, &SsaType::F64, false, false).unwrap();
emit_conv(&mut encoder, &SsaType::I32, true, false).unwrap();
emit_conv(&mut encoder, &SsaType::U32, true, true).unwrap();
let (bytecode, _, _) = encoder.finalize().unwrap();
assert!(!bytecode.is_empty());
}
#[test]
fn test_emit_ldelem_types() {
let mut encoder = InstructionEncoder::new();
emit_ldc_i4(&mut encoder, 0).unwrap(); emit_ldc_i4(&mut encoder, 0).unwrap(); emit_ldelem(&mut encoder, &SsaType::I8).unwrap();
encoder.emit_instruction("pop", None).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_ldelem(&mut encoder, &SsaType::I32).unwrap();
encoder.emit_instruction("pop", None).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_ldelem(&mut encoder, &SsaType::F64).unwrap();
encoder.emit_instruction("pop", None).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_ldelem(&mut encoder, &SsaType::Object).unwrap();
encoder.emit_instruction("pop", None).unwrap();
let (bytecode, _, _) = encoder.finalize().unwrap();
assert!(!bytecode.is_empty());
}
#[test]
fn test_emit_stelem_types() {
let mut encoder = InstructionEncoder::new();
emit_ldc_i4(&mut encoder, 0).unwrap(); emit_ldc_i4(&mut encoder, 0).unwrap(); emit_ldc_i4(&mut encoder, 0).unwrap(); emit_stelem(&mut encoder, &SsaType::I8).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_stelem(&mut encoder, &SsaType::I32).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_stelem(&mut encoder, &SsaType::Object).unwrap();
let (bytecode, _, _) = encoder.finalize().unwrap();
assert!(!bytecode.is_empty());
}
#[test]
fn test_emit_ldind_types() {
let mut encoder = InstructionEncoder::new();
emit_ldc_i4(&mut encoder, 0).unwrap(); emit_ldind(&mut encoder, &SsaType::I8).unwrap();
encoder.emit_instruction("pop", None).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_ldind(&mut encoder, &SsaType::I32).unwrap();
encoder.emit_instruction("pop", None).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_ldind(&mut encoder, &SsaType::Object).unwrap();
encoder.emit_instruction("pop", None).unwrap();
let (bytecode, _, _) = encoder.finalize().unwrap();
assert!(!bytecode.is_empty());
}
#[test]
fn test_emit_stind_types() {
let mut encoder = InstructionEncoder::new();
emit_ldc_i4(&mut encoder, 0).unwrap(); emit_ldc_i4(&mut encoder, 0).unwrap(); emit_stind(&mut encoder, &SsaType::I8).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_stind(&mut encoder, &SsaType::I32).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_ldc_i4(&mut encoder, 0).unwrap();
emit_stind(&mut encoder, &SsaType::Object).unwrap();
let (bytecode, _, _) = encoder.finalize().unwrap();
assert!(!bytecode.is_empty());
}
}