use super::matching::MatchData;
use super::aarch64data::{Command, COND_MAP, SPECIAL_IDENT_MAP, SpecialComm, Relocation};
use super::Context;
use super::ast::{FlatArg, RegKind, RegId, Modifier};
use super::encoding_helpers;
use crate::common::{Stmt, Size, delimited, bitmask};
use crate::parse_helpers::{as_ident, as_unsigned_number, as_float, as_signed_number};
use syn::spanned::Spanned;
use quote::{quote, quote_spanned};
use proc_macro2::{TokenStream, Span};
use proc_macro_error2::emit_error;
pub(super) fn compile_instruction(ctx: &mut Context, data: MatchData) -> Result<(), Option<String>> {
let mut cursor = 0usize;
let mut statics = Vec::new();
let mut dynamics = Vec::new();
let mut relocations = Vec::new();
for command in data.data.commands.iter() {
match *command {
Command::A => {
cursor += 1;
continue
},
Command::C => {
cursor -= 1;
continue
},
Command::Rwidth(offset) => {
statics.push((offset, data.simd_full_width.unwrap_or(true) as u32));
continue
},
_ => ()
}
let arg = data.args.get(cursor).expect("Invalid encoding data, tried to process more arguments than given");
match *arg {
FlatArg::Direct { span, reg: RegKind::Static(id) } => match *command {
Command::R(offset) => {
statics.push((offset, u32::from(id.code())));
},
Command::REven(offset) => {
if id.code() & 1 != 0 {
emit_error!(span, "Field only supports even registers");
return Err(None);
}
statics.push((offset, u32::from(id.code())));
},
Command::RNoZr(offset) => {
if id.code() == 31 {
emit_error!(span, "Field does not support register the zr/sp register");
return Err(None);
}
statics.push((offset, u32::from(id.code())));
},
Command::R4(offset) => {
if id.code() >= 16 {
emit_error!(span, "Field only supports register numbers 0-15");
return Err(None);
}
statics.push((offset, u32::from(id.code())));
},
Command::RNext => {
if let Some(FlatArg::Direct { span: _prevspan, reg: ref prevreg } ) = data.args.get(cursor - 1) {
match prevreg {
RegKind::Static(previd) => if id.code() != ((previd.code() + 1) % 32) {
emit_error!(span, "Invalid register. This register has to be the register after the previous argument.");
return Err(None);
},
RegKind::Dynamic(_, _) => if id != RegId::XZR {
emit_error!(span, "Please use XZR here to indicate that it should be the register after the previous argument.");
return Err(None);
}
}
} else {
panic!("RNext command without the previous command being a register encoder");
}
},
_ => panic!("Invalid argument processor")
},
FlatArg::Direct { span, reg: RegKind::Dynamic(_, ref expr) } => match *command {
Command::R(offset)
| Command::RNoZr(offset) => {
dynamics.push((offset, quote_spanned!{ span=>
#expr & 0x1F
}));
},
Command::REven(offset) => {
dynamics.push((offset, quote_spanned!{ span=>
#expr & 0x1E
}));
},
Command::R4(offset) => {
dynamics.push((offset, quote_spanned!{ span=>
#expr & 0xF
}));
},
Command::RNext => {
emit_error!(span, "This register is constrained to be the register after the previous argument's register. As such, it does not support dynamic registers. Please substitute it with XZR to indicate this");
return Err(None);
},
_ => panic!("Invalid argument processor")
},
FlatArg::Modifier { modifier, .. } => match *command {
Command::Rotates(offset) => match modifier {
Modifier::LSL => statics.push((offset, 0b00)),
Modifier::LSR => statics.push((offset, 0b01)),
Modifier::ASR => statics.push((offset, 0b10)),
Modifier::ROR => statics.push((offset, 0b11)),
_ => panic!("Unexpected modifier for argument processor")
},
Command::ExtendsW(offset) => match modifier {
Modifier::UXTB => statics.push((offset, 0b000)),
Modifier::UXTH => statics.push((offset, 0b001)),
Modifier::UXTW => statics.push((offset, 0b010)),
Modifier::UXTX => statics.push((offset, 0b011)),
Modifier::SXTB => statics.push((offset, 0b100)),
Modifier::SXTH => statics.push((offset, 0b101)),
Modifier::SXTW => statics.push((offset, 0b110)),
Modifier::SXTX => statics.push((offset, 0b111)),
Modifier::LSL => statics.push((offset, 0b010)),
_ => panic!("Unexpected modifier for argument processor")
},
Command::ExtendsX(offset) => match modifier {
Modifier::UXTB => statics.push((offset, 0b000)),
Modifier::UXTH => statics.push((offset, 0b001)),
Modifier::UXTW => statics.push((offset, 0b010)),
Modifier::UXTX => statics.push((offset, 0b011)),
Modifier::SXTB => statics.push((offset, 0b100)),
Modifier::SXTH => statics.push((offset, 0b101)),
Modifier::SXTW => statics.push((offset, 0b110)),
Modifier::SXTX => statics.push((offset, 0b111)),
Modifier::LSL => statics.push((offset, 0b011)),
_ => panic!("Unexpected modifier for argument processor")
},
_ => panic!("Invalid argument processor")
},
FlatArg::Immediate { ref value } => match *command {
Command::Cond(offset) => {
let name = as_ident(value).expect("bad command data").to_string();
let bits = *COND_MAP.get(&&*name).expect("bad command data");
statics.push((offset, u32::from(bits)))
},
Command::CondInv(offset) => {
let name = as_ident(value).expect("bad command data").to_string();
let bits = *COND_MAP.get(&&*name).expect("bad command data");
statics.push((offset, u32::from(bits) ^ 1))
},
Command::LitList(offset, listname) => {
let name = as_ident(value).expect("bad command data").to_string();
let list = SPECIAL_IDENT_MAP.get(listname).expect("bad command data");
if let Some(&bits) = list.get(&&*name) {
statics.push((offset, bits));
} else {
emit_error!(value, "Unknown literal");
return Err(None);
}
},
Command::Ubits(offset, bitlen) => {
let mask = bitmask(bitlen);
if let Some((biased, _)) = static_range_check(value, 0, mask, 0)? {
statics.push((offset, biased));
} else {
let check = dynamic_range_check_unsigned(value.span(), 0, mask, 0);
dynamics.push((offset, quote_spanned!{ value.span()=>
{ let _dyn_imm: u32 = #value; #check; _dyn_imm & #mask }
}));
}
},
Command::Uscaled(offset, bitlen, shift) => {
let mask = bitmask(bitlen);
if let Some((biased, _)) = static_range_check(value, 0, mask, shift)? {
statics.push((offset, biased));
} else {
let check = dynamic_range_check_unsigned(value.span(), 0, mask, shift);
dynamics.push((offset, quote_spanned!{ value.span()=>
{ let _dyn_imm: u32 = #value; #check; (_dyn_imm >> #shift) & #mask }
}));
}
},
Command::Uslice(offset, bitlen, shift) => {
let mask = bitmask(bitlen);
if let Some(value) = as_unsigned_number(value) {
statics.push((offset, ((value as u32) >> shift) & mask));
} else {
dynamics.push((offset, quote_spanned!{ value.span()=>
{ let _dyn_imm: u32 = #value; (#value >> #shift) & #mask }
}));
}
},
Command::Ulist(offset, options) => {
if let Some(number) = as_unsigned_number(value) {
if let Some(i) = options.iter().rposition(|&n| u64::from(n) == number) {
statics.push((offset, i as u32));
} else {
emit_error!(value, "Invalid immediate '{}'", number);
return Err(None);
}
} else {
dynamics.push((offset, quote_spanned!{ value.span()=>
{
let _dyn_imm = #value;
[#(#options),*].iter().rposition(|&n| n as u32 == _dyn_imm)
.unwrap_or_else(|| ::dynasmrt::aarch64::immediate_out_of_range_unsigned_32(_dyn_imm)) as u32
}
}));
}
},
Command::Urange(offset, min, max) => {
let range = u32::from(max - min);
let min = u32::from(min);
if let Some((biased, _)) = static_range_check(value, min as i32, range, 0)? {
statics.push((offset, biased));
} else {
let check = dynamic_range_check_unsigned(value.span(), min, range, 0);
let mask = (range + 1).next_power_of_two() - 1;
dynamics.push((offset, quote_spanned!{ value.span()=>
{ let _dyn_imm: u32 = #value; #check; (_dyn_imm - #min) & #mask }
}));
}
},
Command::Usubone(offset, bitlen) => {
let mask = bitmask(bitlen);
if let Some((biased, _)) = static_range_check(value, 1, mask, 0)? {
statics.push((offset, mask - biased));
} else {
let check = dynamic_range_check_unsigned(value.span(), 1, mask, 0);
let top = mask + 1;
dynamics.push((offset, quote_spanned!{ value.span()=>
{ let _dyn_imm: u32 = #value; #check; (#top - _dyn_imm) & #mask }
}));
}
},
Command::Usubzero(offset, bitlen) => {
let mask = bitmask(bitlen);
if let Some((biased, _)) = static_range_check(value, 0, mask, 0)? {
statics.push((offset, mask - biased));
} else {
let check = dynamic_range_check_unsigned(value.span(), 0, mask, 0);
dynamics.push((offset, quote_spanned!{ value.span()=>
{ let _dyn_imm: u32 = #value; #check; (#mask - _dyn_imm) & #mask }
}));
}
},
Command::Usubmod(offset, bitlen) => {
let mask = bitmask(bitlen);
if let Some((biased, _)) = static_range_check(value, 0, mask, 0)? {
statics.push((offset, biased.wrapping_neg() & mask));
} else {
let check = dynamic_range_check_unsigned(value.span(), 0, mask, 0);
dynamics.push((offset, quote_spanned!{ value.span()=>
{ let _dyn_imm: u32 = #value; #check; _dyn_imm.wrapping_neg() & #mask }
}));
}
},
Command::Usum(offset, bitlen) => {
let mask = bitmask(bitlen);
let prev_value = if let Some(FlatArg::Immediate {value: prev_value } ) = data.args.get(cursor - 1) {
prev_value
} else {
panic!("Bad encoding data, previous argument was not an immediate");
};
let number = if let Some(prev_number) = as_unsigned_number(prev_value) {
if prev_number > mask as u64 {
emit_error!(prev_value, "Impossible immediate combination");
return Err(None);
};
if let Some((biased, _)) = static_range_check(value, 1, mask - (prev_number as u32), 0)? {
Some(biased + (prev_number as u32))
} else {
None
}
} else {
None
};
if let Some(number) = number {
statics.push((offset, number & mask));
} else {
let check = quote_spanned!{ value.span()=>
if (#value - 1u32) > (#mask - #prev_value) { ::dynasmrt::aarch64::immediate_out_of_range_unsigned_32(#value); }
};
dynamics.push((offset, quote_spanned!{ value.span()=>
{ let _dyn_imm: u32 = #value; #check; (#prev_value + _dyn_imm - 1) & #mask }
}));
}
},
Command::Ufields(bitfields) => {
let mask = bitmask(bitfields.len() as u8);
if let Some((biased, _)) = static_range_check(value, 0, mask, 0)? {
for (i, &field) in bitfields.iter().rev().enumerate() {
statics.push((field, (biased >> i) & 1));
}
} else {
let check = dynamic_range_check_unsigned(value.span(), 0, mask, 0);
for (i, &field) in bitfields.iter().rev().enumerate() {
if i == 0 {
dynamics.push((field, quote_spanned!{ value.span()=>
{ let _dyn_imm: u32 = #value; #check; (_dyn_imm >> #i) & 1 }
}));
} else {
dynamics.push((field, quote_spanned!{ value.span()=>
(#value >> #i) & 1
}));
}
}
}
},
Command::Sbits(offset, bitlen) => {
let mask = bitmask(bitlen);
let half = -1i32 << (bitlen - 1);
if let Some((_, scaled)) = static_range_check(value, half, mask, 0)? {
statics.push((offset, scaled & mask));
} else {
let check = dynamic_range_check_signed(value.span(), half, mask, 0);
dynamics.push((offset, quote_spanned!{ value.span()=>
{ let _dyn_imm: i32 = #value; #check; (_dyn_imm as u32) & #mask }
}));
}
},
Command::Sscaled(offset, bitlen, shift) => {
let mask = bitmask(bitlen);
let half = -1i32 << (bitlen - 1);
if let Some((_, scaled)) = static_range_check(value, half, mask, shift)? {
statics.push((offset, scaled & mask));
} else {
let check = dynamic_range_check_signed(value.span(), half, mask, shift);
dynamics.push((offset, quote_spanned!{ value.span()=>
{ let _dyn_imm: i32 = #value; #check; ((_dyn_imm >> #shift) as u32) & #mask }
}));
}
},
Command::Sslice(offset, bitlen, shift) => {
let mask = bitmask(bitlen);
if let Some(value) = as_signed_number(value) {
statics.push((offset, ((value >> shift) as u32) & mask));
} else {
dynamics.push((offset, quote_spanned!{ value.span()=>
{ let _dyn_imm: i32 = #value; ((_dyn_imm >> #shift) as u32) & #mask }
}));
}
},
Command::CUbits(bitlen) => {
let mask = bitmask(bitlen);
if static_range_check(value, 0, mask, 0)?.is_none() {
let check = dynamic_range_check_unsigned(value.span(), 0, mask, 0);
dynamics.push((0, quote_spanned! { value.span()=>
{ let _dyn_imm: u32 = #value; #check; 0 }
}));
}
},
Command::CUsum(bitlen) => {
let mask = bitmask(bitlen);
let prev_value = if let Some(FlatArg::Immediate {value: prev_value } ) = data.args.get(cursor - 1) {
prev_value
} else {
panic!("Bad encoding data, previous argument was not an immediate");
};
let check = if let Some(prev_number) = as_unsigned_number(prev_value) {
if prev_number > mask as u64 {
emit_error!(prev_value, "Impossible immediate combination");
return Err(None);
};
static_range_check(value, 1, mask - (prev_number as u32), 0)?
} else {
None
};
if check.is_none() {
let check = quote_spanned!{ value.span()=>
if (#value - 1u32) > (#mask - #prev_value) { ::dynasmrt::aarch64::immediate_out_of_range_unsigned_32(#value); }
};
dynamics.push((0, quote_spanned! { value.span()=>
{ #check; 0}
}));
}
},
Command::CSscaled(bitlen, shift) => {
let mask = bitmask(bitlen);
let half = -1i32 << (bitlen - 1);
if static_range_check(value, half, mask, shift)?.is_none() {
let check = dynamic_range_check_signed(value.span(), half, mask, shift);
dynamics.push((0, quote_spanned! { value.span()=>
{ let _dyn_imm: i32 = #value; #check; 0 }
}));
}
},
Command::CUrange(min, max) => {
let range = u32::from(max - min);
let min = u32::from(min);
if static_range_check(value, min as i32, range, 0)?.is_none() {
let check = dynamic_range_check_unsigned(value.span(), min, range, 0);
dynamics.push((0, quote_spanned! { value.span()=>
{ let _dyn_imm: u32 = #value; #check; 0 }
}));
}
},
Command::Special(offset, special) => handle_special_immediates(offset, special, value, &mut statics, &mut dynamics)?,
Command::Offset(relocation) => match relocation {
Relocation::B => {
let bits = 26;
let mask = bitmask(bits);
let half = -1i32 << (bits - 1);
if let Some((_, scaled)) = static_range_check(value, half, mask, 2)? {
statics.push((0, scaled & mask));
} else {
let check = dynamic_range_check_signed(value.span(), half, mask, 2);
dynamics.push((0, quote_spanned!{ value.span()=>
{ let _dyn_imm: i32 = #value; #check; ((_dyn_imm >> 2u8) as u32) & #mask }
}));
}
},
Relocation::BCOND => {
let bits = 19;
let mask = bitmask(bits);
let half = -1i32 << (bits - 1);
if let Some((_, scaled)) = static_range_check(value, half, mask, 2)? {
statics.push((5, scaled & mask));
} else {
let check = dynamic_range_check_signed(value.span(), half, mask, 2);
dynamics.push((5, quote_spanned!{ value.span()=>
{ let _dyn_imm: i32 = #value; #check; ((_dyn_imm >> 2u8) as u32) & #mask }
}));
}
},
Relocation::ADR => {
let bits = 21;
let mask = bitmask(bits);
let half = -1i32 << (bits - 1);
if let Some((_, scaled)) = static_range_check(value, half, mask, 0)? {
statics.push((5, (scaled >> 2u8) & 0x7FFFFu32));
statics.push((29, scaled & 3u32));
} else {
let check = dynamic_range_check_signed(value.span(), half, mask, 0);
dynamics.push((5, quote_spanned!{ value.span()=>
{ let _dyn_imm: i32 = #value; #check; ((_dyn_imm >> 2u8) as u32) & 0x7FFFFu32 }
}));
dynamics.push((29, quote_spanned!{ value.span()=>
(#value as u32) & 3u32
}));
}
},
Relocation::ADRP => {
let bits = 21;
let mask = bitmask(bits);
let half = -1i32 << (bits - 1);
if let Some((_, scaled)) = static_range_check(value, half, mask, 12)? {
statics.push((5, scaled & 0x7FFFF));
statics.push((29, scaled & 3));
} else {
let check = dynamic_range_check_signed(value.span(), half, mask, 12);
dynamics.push((5, quote_spanned!{ value.span()=>
{ let _dyn_imm: i32 = #value; #check; ((_dyn_imm >> 14u8) as u32) & 0x7FFFFu32 }
}));
dynamics.push((29, quote_spanned!{ value.span()=>
((#value >> 12u8) as u32) & 3u32
}));
}
},
Relocation::TBZ => {
let bits = 14;
let mask = bitmask(bits);
let half = -1i32 << (bits - 1);
if let Some((_, scaled)) = static_range_check(value, half, mask, 2)? {
statics.push((5, scaled & mask));
} else {
let check = dynamic_range_check_signed(value.span(), half, mask, 2);
dynamics.push((5, quote_spanned!{ value.span()=>
{ let _dyn_imm: i32 = #value; #check; ((_dyn_imm >> 2) as u32) & #mask }
}));
}
},
Relocation::LITERAL8
| Relocation::LITERAL16
| Relocation::LITERAL32
| Relocation::LITERAL64 => ()
},
_ => panic!("Invalid argument processor")
},
FlatArg::Default => match *command {
Command::R(offset) => {
statics.push((offset, 0b11111u32));
},
Command::Rotates(offset) => {
statics.push((offset, 0b00));
},
Command::ExtendsW(offset) => {
statics.push((offset, 0b010));
},
Command::ExtendsX(offset) => {
statics.push((offset, 0b011));
},
Command::Ubits(_, _) |
Command::Uscaled(_, _, _) |
Command::Uslice(_, _, _) |
Command::Urange(_, _, _) |
Command::Ulist(_, _) |
Command::Ufields(_) |
Command::Sbits(_, _) |
Command::Sscaled(_, _, _) |
Command::Sslice(_, _, _) => (),
Command::CUbits(_) |
Command::CSscaled(_, _) => (),
_ => panic!("Invalid argument processor")
},
FlatArg::JumpTarget { ref jump } => match *command {
Command::Offset(relocation) => {
let stmt = jump.clone().encode(4, 4, &[relocation.to_id()]);
relocations.push(stmt);
},
_ => panic!("Invalid argument processor")
},
FlatArg::Lit { ref ident } => match *command {
Command::Cond(offset) => {
let name = ident.to_string();
let bits = *COND_MAP.get(&&*name).expect("bad command data");
statics.push((offset, u32::from(bits)))
},
Command::CondInv(offset) => {
let name = ident.to_string();
let bits = *COND_MAP.get(&&*name).expect("bad command data");
statics.push((offset, u32::from(bits) ^ 1))
},
Command::LitList(offset, listname) => {
let name = ident.to_string();
let list = SPECIAL_IDENT_MAP.get(listname).expect("bad command data");
if let Some(&bits) = list.get(&&*name) {
statics.push((offset, bits));
} else {
emit_error!(ident, "Unknown literal");
return Err(None);
}
},
_ => panic!("Invalid argument processor")
}
}
match *command {
Command::Uslice(_, _, _) |
Command::Sslice(_, _, _) => (),
Command::CUbits(_) |
Command::CUsum(_) |
Command::CSscaled(_, _) |
Command::CUrange(_, _) => (),
_ => cursor += 1
}
}
if cursor != data.args.len() {
panic!("Not enough command processors");
}
let mut bits = data.data.base;
for (offset, value) in statics {
bits |= value << offset;
}
if !dynamics.is_empty() {
let mut res = quote!{
#bits
};
for (offset, expr) in dynamics {
res = quote!{
#res | ((#expr) << #offset)
};
}
ctx.state.stmts.push(Stmt::ExprUnsigned(delimited(res), Size::B_4));
} else {
ctx.state.stmts.push(Stmt::Const(u64::from(bits), Size::B_4));
}
ctx.state.stmts.extend(relocations);
Ok(())
}
fn handle_special_immediates(offset: u8, special: SpecialComm, imm: &syn::Expr, statics: &mut Vec<(u8, u32)>, dynamics: &mut Vec<(u8, TokenStream)>) -> Result<(), Option<String>> {
match special {
SpecialComm::INVERTED_WIDE_IMMEDIATE_X => if let Some(number) = None::<u64> { if let Some(encoded) = encoding_helpers::encode_wide_immediate_64bit(!number) {
statics.push((offset, encoded));
return Ok(());
}
} else {
dynamics.push((offset, quote_spanned!{ imm.span()=>
{
let value: u64 = !#imm;
let offset = value.trailing_zeros() & 0b110000;
if (value & !(0xFFFFu64 << offset)) != 0 {
::dynasmrt::aarch64::immediate_out_of_range_unsigned_64(!value);
}
((0xFFFFu64 & (value >> offset)) as u32) | (offset << 12)
}
}));
return Ok(());
},
SpecialComm::INVERTED_WIDE_IMMEDIATE_W => if let Some(number) = as_unsigned_number(imm) {
if number <= u64::from(u32::MAX) {
if let Some(encoded) = encoding_helpers::encode_wide_immediate_32bit(!(number as u32)) {
statics.push((offset, encoded));
return Ok(());
}
}
} else {
dynamics.push((offset, quote_spanned!{ imm.span()=>
{
let value: u32 = !#imm;
let offset = value.trailing_zeros() & 0b10000;
if (value & !(0xFFFFu32 << offset)) != 0 {
::dynasmrt::aarch64::immediate_out_of_range_unsigned_32(!value);
}
(0xFFFFu32 & (value >> offset)) | (offset << 12)
}
}));
return Ok(());
},
SpecialComm::WIDE_IMMEDIATE_X => if let Some(number) = as_unsigned_number(imm) {
if let Some(encoded) = encoding_helpers::encode_wide_immediate_64bit(number) {
statics.push((offset, encoded));
return Ok(());
}
} else {
dynamics.push((offset, quote_spanned!{ imm.span()=>
{
let value: u64 = #imm;
let offset = value.trailing_zeros() & 0b110000;
if (value & !(0xFFFFu64 << offset)) != 0 {
::dynasmrt::aarch64::immediate_out_of_range_unsigned_64(value);
}
((0xFFFFu64 & (value >> offset)) as u32) | (offset << 12)
}
}));
return Ok(());
},
SpecialComm::WIDE_IMMEDIATE_W => if let Some(number) = as_unsigned_number(imm) {
if number <= u64::from(u32::MAX) {
if let Some(encoded) = encoding_helpers::encode_wide_immediate_32bit(number as u32) {
statics.push((offset, encoded));
return Ok(());
}
}
} else {
dynamics.push((offset, quote_spanned!{ imm.span()=>
{
let value: u32 = #imm;
let offset = value.trailing_zeros() & 0b10000;
if (value & !(0xFFFFu32 << offset)) != 0 {
::dynasmrt::aarch64::immediate_out_of_range_unsigned_32(value);
}
(0xFFFFu32 & (value >> offset)) | (offset << 12)
}
}));
return Ok(());
},
SpecialComm::STRETCHED_IMMEDIATE => if let Some(number) = as_unsigned_number(imm) {
if let Some(encoded) = encoding_helpers::encode_stretched_immediate(number) {
statics.push((offset, encoded & 0x1F));
statics.push((offset + 6, encoded & 0xE0));
return Ok(());
}
} else {
dynamics.push((offset, quote_spanned!{ imm.span()=>
{
let value: u64 = #imm;
let mut test = value & 0x0101_0101_0101_0101;
test |= test << 1;
test |= test << 2;
test |= test << 4;
if test != value {
::dynasmrt::aarch64::immediate_out_of_range_unsigned_64(value);
}
let mut masked = value & 0x8040201008040201;
masked |= masked >> 32;
masked |= masked >> 16;
masked |= masked >> 8;
let masked = masked as u32;
((masked & 0xE0) << 6) | (masked & 0x1F)
}
}));
return Ok(());
},
SpecialComm::LOGICAL_IMMEDIATE_W => if let Some(number) = as_unsigned_number(imm) {
if number <= u64::from(u32::MAX) {
if let Some(encoded) = encoding_helpers::encode_logical_immediate_32bit(number as u32) {
statics.push((offset, u32::from(encoded)));
return Ok(());
}
}
} else {
dynamics.push((offset, quote_spanned!{ imm.span()=>
dynasmrt::aarch64::encode_logical_immediate_32bit(#imm).unwrap_or_else(
|| ::dynasmrt::aarch64::immediate_out_of_range_unsigned_32(#imm)) as u32
}));
return Ok(());
},
SpecialComm::LOGICAL_IMMEDIATE_X => if let Some(number) = as_unsigned_number(imm) {
if let Some(encoded) = encoding_helpers::encode_logical_immediate_64bit(number) {
statics.push((offset, u32::from(encoded)));
return Ok(());
}
} else {
dynamics.push((offset, quote_spanned!{ imm.span()=>
dynasmrt::aarch64::encode_logical_immediate_64bit(#imm).unwrap_or_else(
|| ::dynasmrt::aarch64::immediate_out_of_range_unsigned_64(#imm)) as u32
}));
return Ok(());
},
SpecialComm::FLOAT_IMMEDIATE => if let Some(number) = as_float(imm) {
if let Some(encoded) = encoding_helpers::encode_floating_point_immediate(number as f32) {
statics.push((offset, u32::from(encoded)));
return Ok(());
}
} else {
dynamics.push((offset, quote_spanned!{ imm.span()=>
{
let value: f32 = #imm;
let bits = value.to_bits();
let check = (bits >> 25) & 0x3F;
if (check != 0b10_0000 && check != 0b01_1111) || (bits & 0x7_FFFF) != 0 {
::dynasmrt::aarch64::immediate_out_of_range_unsigned_f32(value);
}
((bits >> 24) & 0x80) | ((bits >> 19) & 0x7F)
}
}));
return Ok(());
},
SpecialComm::SPLIT_FLOAT_IMMEDIATE => if let Some(number) = as_float(imm) {
if let Some(encoded) = encoding_helpers::encode_floating_point_immediate(number as f32) {
statics.push((offset, u32::from(encoded & 0x1F)));
statics.push((offset + 6, u32::from(encoded & 0xE0)));
return Ok(());
}
} else {
dynamics.push((offset, quote_spanned!{ imm.span()=>
{
let value: f32 = #imm;
let bits = value.to_bits();
let check = (bits >> 25) & 0x3F;
if (check != 0b10_0000 && check != 0b01_1111) || (bits & 0x7_FFFF) != 0 {
::dynasmrt::aarch64::immediate_out_of_range_unsigned_f32(value);
}
((bits >> 18) & 0x20_00) | ((bits >> 13) & 0x18_00) | ((bits >> 19) & 0x1F)
}
}));
return Ok(());
},
}
emit_error!(imm, "Unrepresentable immediate");
Err(None)
}
fn static_range_check(expr: &syn::Expr, bias: i32, range: u32, scale: u8) -> Result<Option<(u32, u32)>, Option<String>> {
#![allow(unexpected_cfgs)]
let value = match as_signed_number(expr) {
Some(v) => v,
None => return Ok(None)
};
#[cfg(disable_static_checks="1")]
return Ok(None);
let scaled: i64 = value >> scale;
if scaled << scale != value {
emit_error!(expr, "Unrepresentable immediate");
return Err(None);
}
let biased = scaled - i64::from(bias);
if biased < 0 {
emit_error!(expr, "Immediate too low");
Err(None)
} else if biased > i64::from(range) {
emit_error!(expr, "Immediate too high");
Err(None)
} else {
Ok(Some((biased as u32, scaled as u32)))
}
}
fn dynamic_range_check_unsigned(span: Span, bias: u32, range: u32, scale: u8) -> TokenStream {
let check = if scale == 0 {
if bias == 0 {
quote_spanned!{ span=> _dyn_imm > #range }
} else {
quote_spanned!{ span=> _dyn_imm.wrapping_sub(#bias) > #range }
}
} else {
let mask = bitmask(scale);
if bias == 0 {
quote_spanned!{ span=> ((_dyn_imm & #mask) != 0) || (_dyn_imm >> #scale) > #range }
} else {
quote_spanned!{ span=> ((_dyn_imm & #mask) != 0) || (_dyn_imm >> #scale).wrapping_sub(#bias) > #range }
}
};
quote_spanned!{ span => if #check { ::dynasmrt::aarch64::immediate_out_of_range_unsigned_32(_dyn_imm); }}
}
fn dynamic_range_check_signed(span: Span, bias: i32, range: u32, scale: u8) -> TokenStream {
let bias = -bias;
let check = if scale == 0 {
if bias == 0 {
quote_spanned!{ span => (_dyn_imm as u32) > #range }
} else {
quote_spanned!{ span => (_dyn_imm.wrapping_add(#bias) as u32) > #range }
}
} else {
let mask = bitmask(scale) as i32;
if bias == 0 {
quote_spanned!{ span=> ((_dyn_imm & #mask) != 0) || ((_dyn_imm >> #scale) as u32) > #range }
} else {
quote_spanned!{ span=> ((_dyn_imm & #mask) != 0) || ((_dyn_imm >> #scale).wrapping_add(#bias) as u32) > #range }
}
};
quote_spanned!{ span => if #check { ::dynasmrt::aarch64::immediate_out_of_range_signed_32(_dyn_imm); }}
}