Skip to content

Commit

Permalink
better __transmute const evaluation
Browse files Browse the repository at this point in the history
  • Loading branch information
xunilrj committed Nov 13, 2024
1 parent 61f75b0 commit 50e70b1
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 66 deletions.
158 changes: 98 additions & 60 deletions sway-core/src/ir_generation/const_eval.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::ops::{BitAnd, BitOr, BitXor, Not, Rem};
use std::{
io::Read,
ops::{BitAnd, BitOr, BitXor, Not, Rem},
};

use crate::{
engine_threading::*,
Expand Down Expand Up @@ -1384,78 +1387,113 @@ fn const_eval_intrinsic(
)
.unwrap();

fn bytes_to_uint<'a, const N: usize>(
context: &mut Context<'_>,
items: impl Iterator<Item = &'a Constant>,
) -> [u8; N]
where
[u8; N]: TryFrom<Vec<u8>>,
<[u8; N] as TryFrom<Vec<u8>>>::Error: std::fmt::Debug,
{
let mut bytes = Vec::<u8>::with_capacity(N);
// check IR sizes match
let src_ir_type_in_bytes = src_ir_type.size(lookup.context).in_bytes();
let dst_ir_type_in_bytes = dst_ir_type.size(lookup.context).in_bytes();
if src_ir_type_in_bytes != dst_ir_type_in_bytes {
return Err(ConstEvalError::CompileError);
}

for item in items {
assert!(item.ty.is_uint8(context));
match item.value {
fn append_bytes(
ctx: &Context<'_>,
bytes: &mut Vec<u8>,
t: &Type,
value: &ConstantValue,
) {
match t.get_content(ctx) {
TypeContent::Array(item_type, size) => match value {
ConstantValue::Array(items) => {
assert!(*size as usize == items.len());
for item in items {
append_bytes(ctx, bytes, item_type, &item.value);
}
}
_ => unreachable!(),
},
TypeContent::Uint(8) => match value {
ConstantValue::Uint(v) => {
let v = v.try_into().unwrap();
bytes.push(v);
bytes.extend((*v as u8).to_be_bytes());
}
_ => unreachable!(),
};
},
TypeContent::Uint(16) => match value {
ConstantValue::Uint(v) => {
bytes.extend([0u8, 0u8, 0u8, 0u8, 0u8, 0u8]);
bytes.extend((*v as u16).to_be_bytes());
}
_ => unreachable!(),
},
TypeContent::Uint(32) => match value {
ConstantValue::Uint(v) => {
bytes.extend([0u8, 0u8, 0u8, 0u8]);
bytes.extend((*v as u32).to_be_bytes());
}
_ => unreachable!(),
},
TypeContent::Uint(64) => match value {
ConstantValue::Uint(v) => {
bytes.extend((*v).to_be_bytes());
}
_ => unreachable!(),
},
x => todo!("{:?}", x),
}

bytes.try_into().unwrap()
}

fn check_is_zero<'a>(items: &mut impl Iterator<Item = &'a Constant>, n: usize) {
for item in items.by_ref().take(n) {
if item.as_uint().unwrap() != 0 {
todo!();
fn transmute_bytes(
ctx: &Context<'_>,
bytes: &mut std::io::Cursor<Vec<u8>>,
t: &Type,
) -> Constant {
match t.get_content(ctx) {
TypeContent::Uint(8) => {
let mut buffer = [0u8];
let _ = bytes.read_exact(&mut buffer);
Constant {
ty: Type::get_uint8(ctx),
value: ConstantValue::Uint(buffer[0] as u64),
}
}
}
}

// TODO check sizes
match (
src_ir_type.get_content(lookup.context),
dst_ir_type.get_content(lookup.context),
&args[0].value,
) {
(
TypeContent::Array(item_type, 8),
TypeContent::Uint(64),
ConstantValue::Array(items),
) if item_type.is_uint8(lookup.context) => {
assert!(items.len() == 8);

let mut items = items.iter();
let value = match &*lookup.engines.te().get(intrinsic.type_arguments[1].type_id)
{
TypeInfo::UnsignedInteger(IntegerBits::Sixteen) => {
check_is_zero(&mut items, 6);
let bytes = bytes_to_uint::<2>(lookup.context, items);
ConstantValue::Uint(u16::from_be_bytes(bytes) as u64)
TypeContent::Uint(16) => {
let mut buffer = [0u8; 8]; // u16 = u64 at runtime
let _ = bytes.read_exact(&mut buffer);
let buffer = [buffer[6], buffer[7]];
Constant {
ty: Type::get_uint16(ctx),
value: ConstantValue::Uint(u16::from_be_bytes(buffer) as u64),
}
TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo) => {
check_is_zero(&mut items, 4);
let bytes = bytes_to_uint::<4>(lookup.context, items);
ConstantValue::Uint(u32::from_be_bytes(bytes) as u64)
}
TypeContent::Uint(32) => {
let mut buffer = [0u8; 8]; // u32 = u64 at runtime
let _ = bytes.read_exact(&mut buffer);
let buffer = [buffer[4], buffer[5], buffer[6], buffer[7]];
Constant {
ty: Type::get_uint32(ctx),
value: ConstantValue::Uint(u32::from_be_bytes(buffer) as u64),
}
TypeInfo::UnsignedInteger(IntegerBits::SixtyFour) => {
let bytes = bytes_to_uint::<8>(lookup.context, items);
ConstantValue::Uint(u64::from_be_bytes(bytes))
}
TypeContent::Uint(64) => {
let mut buffer = [0u8; 8];
let _ = bytes.read_exact(&mut buffer);
Constant {
ty: Type::get_uint64(ctx),
value: ConstantValue::Uint(u64::from_be_bytes(buffer)),
}
_ => todo!(),
};

Ok(Some(Constant {
ty: Type::get_uint64(lookup.context),
value,
}))
}
_ => todo!(),
}
_ => todo!(),
}

let mut runtime_bytes = vec![];
append_bytes(
lookup.context,
&mut runtime_bytes,
&src_ir_type,
&args[0].value,
);
let mut cursor = std::io::Cursor::new(runtime_bytes);
let c = transmute_bytes(lookup.context, &mut cursor, &dst_ir_type);
Ok(Some(c))
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions sway-core/src/ir_generation/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2180,12 +2180,12 @@ impl<'eng> FnCompiler<'eng> {
Intrinsic::Slice => self.compile_intrinsic_slice(arguments, context, md_mgr),
Intrinsic::ElemAt => self.compile_intrinsic_elem_at(arguments, context, md_mgr),
Intrinsic::Transmute => {
self.compile_intrins_transmute(arguments, return_type, context, md_mgr)
self.compile_intrinsic_transmute(arguments, return_type, context, md_mgr)
}
}
}

fn compile_intrins_transmute(
fn compile_intrinsic_transmute(
&mut self,
arguments: &[ty::TyExpression],
return_type: TypeId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,6 @@ fn type_check_transmute(
ty::TyExpression::type_check(handler, ctx, &arguments[0]).unwrap()
};

// if handler.has_errors() {
// panic!("{:?}", handler.clone().consume());
// }

Ok((
TyIntrinsicFunctionKind {
kind,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ fn const_transmute() {

const U8ARRAY_U64 = __transmute::<[u8; 8], u64>([1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8]);
assert(U8ARRAY_U64 == 0x0102030405060708u64);

// u32 <-> u64
const U32_U64 = __transmute::<u32, u64>(1u32);
assert(U32_U64 == 0x0000000000000001u64);

const U64_U32 = __transmute::<u64, u32>(1u64);
assert(U64_U32 == 0x00000001u32);
}

fn main() {
Expand Down Expand Up @@ -62,6 +69,13 @@ fn main() {
let u8array_u64 = __transmute::<[u8; 8], u64>([0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8]);
assert(u8array_u64 == 1);

// u32 <-> u64
let u32_u64 = __transmute::<u32, u64>(1u32);
assert(u32_u64 == 0x0000000000000001u64);

let u64_u32 = __transmute::<u64, u32>(1u64);
assert(u64_u32 == 0x00000001u32);

// check u256 and b256 are transmutable
let u256_b256 = __transmute::<u256, b256>(u256::max());
assert(u256_b256 == b256::max());
Expand Down

0 comments on commit 50e70b1

Please # to comment.