feat: add support for *T pointers
This commit is contained in:
@@ -137,14 +137,27 @@ impl CraneliftBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut var_map = HashMap::new();
|
let mut var_map = HashMap::new();
|
||||||
|
let mut stack_slot_map = HashMap::new();
|
||||||
for local in &func.locals {
|
for local in &func.locals {
|
||||||
|
if local.address_taken {
|
||||||
|
let cl_ty = Self::lower_type(&local.ty);
|
||||||
|
let bytes = cl_ty.bytes();
|
||||||
|
let slot = builder.create_sized_stack_slot(ir::StackSlotData::new(
|
||||||
|
ir::StackSlotKind::ExplicitSlot,
|
||||||
|
bytes,
|
||||||
|
0,
|
||||||
|
));
|
||||||
|
stack_slot_map.insert(local.id, slot);
|
||||||
|
} else {
|
||||||
let var = builder.declare_var(Self::lower_type(&local.ty));
|
let var = builder.declare_var(Self::lower_type(&local.ty));
|
||||||
var_map.insert(local.id, var);
|
var_map.insert(local.id, var);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut trans = FunctionTranslator {
|
let mut trans = FunctionTranslator {
|
||||||
builder,
|
builder,
|
||||||
var_map,
|
var_map,
|
||||||
|
stack_slot_map,
|
||||||
block_map,
|
block_map,
|
||||||
locals: &func.locals,
|
locals: &func.locals,
|
||||||
module: &mut self.module,
|
module: &mut self.module,
|
||||||
@@ -166,9 +179,13 @@ impl CraneliftBackend {
|
|||||||
if i == 0 {
|
if i == 0 {
|
||||||
for (j, param_id) in func.params.iter().enumerate() {
|
for (j, param_id) in func.params.iter().enumerate() {
|
||||||
let val = trans.builder.block_params(cl_block)[j];
|
let val = trans.builder.block_params(cl_block)[j];
|
||||||
|
if let Some(&slot) = trans.stack_slot_map.get(param_id) {
|
||||||
|
trans.builder.ins().stack_store(val, slot, 0);
|
||||||
|
} else {
|
||||||
trans.builder.def_var(trans.var_map[param_id], val);
|
trans.builder.def_var(trans.var_map[param_id], val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for stmt in &block.statements {
|
for stmt in &block.statements {
|
||||||
trans.translate_stmt(stmt);
|
trans.translate_stmt(stmt);
|
||||||
@@ -195,6 +212,7 @@ impl CraneliftBackend {
|
|||||||
Ty::F32 => types::F32,
|
Ty::F32 => types::F32,
|
||||||
Ty::F64 => types::F64,
|
Ty::F64 => types::F64,
|
||||||
Ty::Bool => types::I8, // Booleans are represented as 8-bit integers
|
Ty::Bool => types::I8, // Booleans are represented as 8-bit integers
|
||||||
|
Ty::Pointer(_) => types::I64, // Assume 64-bit environment pointers
|
||||||
_ => unimplemented!("Unsupported type for Cranelift lowering: {:?}", ty),
|
_ => unimplemented!("Unsupported type for Cranelift lowering: {:?}", ty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,6 +223,7 @@ impl CraneliftBackend {
|
|||||||
struct FunctionTranslator<'a> {
|
struct FunctionTranslator<'a> {
|
||||||
builder: FunctionBuilder<'a>,
|
builder: FunctionBuilder<'a>,
|
||||||
var_map: HashMap<LocalId, Variable>,
|
var_map: HashMap<LocalId, Variable>,
|
||||||
|
stack_slot_map: HashMap<LocalId, ir::StackSlot>,
|
||||||
block_map: HashMap<BlockId, ir::Block>,
|
block_map: HashMap<BlockId, ir::Block>,
|
||||||
locals: &'a [LocalDecl],
|
locals: &'a [LocalDecl],
|
||||||
module: &'a mut ObjectModule,
|
module: &'a mut ObjectModule,
|
||||||
@@ -217,13 +236,25 @@ impl<'a> FunctionTranslator<'a> {
|
|||||||
StatementKind::Assign(local_id, rvalue) => {
|
StatementKind::Assign(local_id, rvalue) => {
|
||||||
let val = self.translate_rvalue(rvalue);
|
let val = self.translate_rvalue(rvalue);
|
||||||
if let Some(v) = val {
|
if let Some(v) = val {
|
||||||
|
if let Some(&slot) = self.stack_slot_map.get(local_id) {
|
||||||
|
self.builder.ins().stack_store(v, slot, 0);
|
||||||
|
} else {
|
||||||
let var = self.var_map[local_id];
|
let var = self.var_map[local_id];
|
||||||
self.builder.def_var(var, v);
|
self.builder.def_var(var, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
StatementKind::SideEffect(rvalue) => {
|
StatementKind::SideEffect(rvalue) => {
|
||||||
self.translate_rvalue(rvalue);
|
self.translate_rvalue(rvalue);
|
||||||
}
|
}
|
||||||
|
StatementKind::Store { ptr, val } => {
|
||||||
|
let ptr_val = self.translate_operand(ptr);
|
||||||
|
if let Some(v) = self.translate_rvalue(val) {
|
||||||
|
self.builder
|
||||||
|
.ins()
|
||||||
|
.store(ir::MemFlags::trusted(), v, ptr_val, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,9 +303,14 @@ impl<'a> FunctionTranslator<'a> {
|
|||||||
fn translate_operand(&mut self, op: &Operand) -> ir::Value {
|
fn translate_operand(&mut self, op: &Operand) -> ir::Value {
|
||||||
match op {
|
match op {
|
||||||
Operand::Copy(local_id) => {
|
Operand::Copy(local_id) => {
|
||||||
|
if let Some(&slot) = self.stack_slot_map.get(local_id) {
|
||||||
|
let cl_ty = CraneliftBackend::lower_type(&self.locals[local_id.0].ty);
|
||||||
|
self.builder.ins().stack_load(cl_ty, slot, 0)
|
||||||
|
} else {
|
||||||
let var = self.var_map[local_id];
|
let var = self.var_map[local_id];
|
||||||
self.builder.use_var(var)
|
self.builder.use_var(var)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Operand::Constant(ConstantValue::Integer(val, ty)) => {
|
Operand::Constant(ConstantValue::Integer(val, ty)) => {
|
||||||
let cl_ty = CraneliftBackend::lower_type(ty);
|
let cl_ty = CraneliftBackend::lower_type(ty);
|
||||||
self.builder.ins().iconst(cl_ty, *val as i64)
|
self.builder.ins().iconst(cl_ty, *val as i64)
|
||||||
@@ -315,6 +351,9 @@ impl<'a> FunctionTranslator<'a> {
|
|||||||
.icmp(ir::condcodes::IntCC::Equal, inner_val, zero),
|
.icmp(ir::condcodes::IntCC::Equal, inner_val, zero),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
UnaryOp::Deref | UnaryOp::AddressOf => {
|
||||||
|
unreachable!("handled as distinct Rvalues in MIR")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Rvalue::BinaryOp(op, lhs, rhs) => {
|
Rvalue::BinaryOp(op, lhs, rhs) => {
|
||||||
@@ -527,6 +566,24 @@ impl<'a> FunctionTranslator<'a> {
|
|||||||
Some(self.builder.inst_results(call_inst)[0])
|
Some(self.builder.inst_results(call_inst)[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Rvalue::AddressOf(local_id) => {
|
||||||
|
let slot = self.stack_slot_map[local_id];
|
||||||
|
Some(self.builder.ins().stack_addr(types::I64, slot, 0))
|
||||||
|
}
|
||||||
|
Rvalue::ReadPointer(ptr_op) => {
|
||||||
|
let ptr_val = self.translate_operand(ptr_op);
|
||||||
|
let ptr_ty = self.get_operand_type(ptr_op);
|
||||||
|
let inner_ty = match ptr_ty {
|
||||||
|
Ty::Pointer(inner) => *inner,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let cl_ty = CraneliftBackend::lower_type(&inner_ty);
|
||||||
|
Some(
|
||||||
|
self.builder
|
||||||
|
.ins()
|
||||||
|
.load(cl_ty, ir::MemFlags::trusted(), ptr_val, 0),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ pub enum TypeKind {
|
|||||||
F32,
|
F32,
|
||||||
F64,
|
F64,
|
||||||
Bool,
|
Bool,
|
||||||
|
Pointer(Box<Type>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@@ -177,6 +178,8 @@ pub enum ExprKind<P: Phase = Untyped> {
|
|||||||
pub enum UnaryOp {
|
pub enum UnaryOp {
|
||||||
Neg,
|
Neg,
|
||||||
Not,
|
Not,
|
||||||
|
Deref,
|
||||||
|
AddressOf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ impl<'src> Lexer<'src> {
|
|||||||
'*' => token!(TokenKind::Star),
|
'*' => token!(TokenKind::Star),
|
||||||
'/' => token!(TokenKind::Slash),
|
'/' => token!(TokenKind::Slash),
|
||||||
'%' => token!(TokenKind::Percent),
|
'%' => token!(TokenKind::Percent),
|
||||||
|
'&' => token!(TokenKind::Ampersand),
|
||||||
|
|
||||||
'!' => token!(TokenKind::Bang, '=' => TokenKind::Unequal),
|
'!' => token!(TokenKind::Bang, '=' => TokenKind::Unequal),
|
||||||
'=' => token!(TokenKind::Assign, '=' => TokenKind::Equal),
|
'=' => token!(TokenKind::Assign, '=' => TokenKind::Equal),
|
||||||
|
|||||||
@@ -314,6 +314,15 @@ impl<'src> Parser<'src> {
|
|||||||
self.advance();
|
self.advance();
|
||||||
TypeKind::Bool
|
TypeKind::Bool
|
||||||
}
|
}
|
||||||
|
TokenKind::Star => {
|
||||||
|
let star_token = self.advance().unwrap();
|
||||||
|
let inner = self.parse_type()?;
|
||||||
|
let span = star_token.span.join(inner.span);
|
||||||
|
return Ok(Type {
|
||||||
|
kind: TypeKind::Pointer(Box::new(inner)),
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParseError::new(
|
return Err(ParseError::new(
|
||||||
@@ -782,6 +791,8 @@ impl<'src> Parser<'src> {
|
|||||||
match op {
|
match op {
|
||||||
TokenKind::Minus => Some((UnaryOp::Neg, 30)),
|
TokenKind::Minus => Some((UnaryOp::Neg, 30)),
|
||||||
TokenKind::Bang => Some((UnaryOp::Not, 30)),
|
TokenKind::Bang => Some((UnaryOp::Not, 30)),
|
||||||
|
TokenKind::Star => Some((UnaryOp::Deref, 30)),
|
||||||
|
TokenKind::Ampersand => Some((UnaryOp::AddressOf, 30)),
|
||||||
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|||||||
+85
-6
@@ -41,6 +41,7 @@ pub enum Ty {
|
|||||||
Unit,
|
Unit,
|
||||||
Var(usize),
|
Var(usize),
|
||||||
Function(Vec<Ty>, Box<Ty>),
|
Function(Vec<Ty>, Box<Ty>),
|
||||||
|
Pointer(Box<Ty>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ty {
|
impl Ty {
|
||||||
@@ -85,6 +86,11 @@ impl Ty {
|
|||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the type is a pointer.
|
||||||
|
pub fn is_pointer(&self) -> bool {
|
||||||
|
matches!(self, Ty::Pointer(_))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&TypeKind> for Ty {
|
impl From<&TypeKind> for Ty {
|
||||||
@@ -101,6 +107,20 @@ impl From<&TypeKind> for Ty {
|
|||||||
TypeKind::F32 => Ty::F32,
|
TypeKind::F32 => Ty::F32,
|
||||||
TypeKind::F64 => Ty::F64,
|
TypeKind::F64 => Ty::F64,
|
||||||
TypeKind::Bool => Ty::Bool,
|
TypeKind::Bool => Ty::Bool,
|
||||||
|
TypeKind::Pointer(inner) => Ty::Pointer(Box::new(Ty::from(&inner.kind))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypedExpr {
|
||||||
|
/// Returns `true` if the expression produces a valid memory location that can be mutated.
|
||||||
|
pub fn is_lvalue(&self) -> bool {
|
||||||
|
match &self.kind {
|
||||||
|
TypedExprKind::Identifier { .. } => true,
|
||||||
|
TypedExprKind::Unary {
|
||||||
|
op: UnaryOp::Deref, ..
|
||||||
|
} => true,
|
||||||
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,6 +207,8 @@ impl Sema {
|
|||||||
Ty::Function(params, ret)
|
Ty::Function(params, ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ty::Pointer(inner) => Ty::Pointer(Box::new(self.apply_subst(inner))),
|
||||||
|
|
||||||
t => t.clone(),
|
t => t.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,6 +222,7 @@ impl Sema {
|
|||||||
Ty::Function(params, ret) => {
|
Ty::Function(params, ret) => {
|
||||||
params.iter().any(|p| self.occurs_check(v, p)) || self.occurs_check(v, &ret)
|
params.iter().any(|p| self.occurs_check(v, p)) || self.occurs_check(v, &ret)
|
||||||
}
|
}
|
||||||
|
Ty::Pointer(inner) => self.occurs_check(v, &inner),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,6 +258,7 @@ impl Sema {
|
|||||||
|
|
||||||
self.unify(&r1, &r2)
|
self.unify(&r1, &r2)
|
||||||
}
|
}
|
||||||
|
(Ty::Pointer(p1), Ty::Pointer(p2)) => self.unify(&p1, &p2),
|
||||||
(a, b) => Err(format!("type mismatch: expected {:?}, found {:?}", a, b)),
|
(a, b) => Err(format!("type mismatch: expected {:?}, found {:?}", a, b)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -617,6 +641,48 @@ impl Sema {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExprKind::Unary {
|
||||||
|
op: UnaryOp::AddressOf,
|
||||||
|
expr: inner_expr,
|
||||||
|
} => {
|
||||||
|
let typed_inner = self.analyze_expr(inner_expr);
|
||||||
|
if !typed_inner.is_lvalue() {
|
||||||
|
self.errors.push(SemanticError::new(
|
||||||
|
"invalid operand for address-of operator",
|
||||||
|
inner_expr.span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
TypedExpr {
|
||||||
|
ty: Ty::Pointer(Box::new(typed_inner.ty.clone())),
|
||||||
|
kind: TypedExprKind::Unary {
|
||||||
|
op: UnaryOp::AddressOf,
|
||||||
|
expr: Box::new(typed_inner),
|
||||||
|
},
|
||||||
|
span: expr.span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExprKind::Unary {
|
||||||
|
op: UnaryOp::Deref,
|
||||||
|
expr: inner_expr,
|
||||||
|
} => {
|
||||||
|
let typed_inner = self.analyze_expr(inner_expr);
|
||||||
|
let result_ty = self.new_var();
|
||||||
|
if let Err(e) =
|
||||||
|
self.unify(&typed_inner.ty, &Ty::Pointer(Box::new(result_ty.clone())))
|
||||||
|
{
|
||||||
|
self.errors.push(SemanticError::new(e, inner_expr.span));
|
||||||
|
}
|
||||||
|
TypedExpr {
|
||||||
|
kind: TypedExprKind::Unary {
|
||||||
|
op: UnaryOp::Deref,
|
||||||
|
expr: Box::new(typed_inner),
|
||||||
|
},
|
||||||
|
ty: result_ty,
|
||||||
|
span: expr.span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ExprKind::Binary { op, lhs, rhs } => {
|
ExprKind::Binary { op, lhs, rhs } => {
|
||||||
let typed_lhs = self.analyze_expr(lhs);
|
let typed_lhs = self.analyze_expr(lhs);
|
||||||
let typed_rhs = self.analyze_expr(rhs);
|
let typed_rhs = self.analyze_expr(rhs);
|
||||||
@@ -657,12 +723,11 @@ impl Sema {
|
|||||||
let typed_rval = self.analyze_expr(rval);
|
let typed_rval = self.analyze_expr(rval);
|
||||||
let typed_lval = self.analyze_expr(lval);
|
let typed_lval = self.analyze_expr(lval);
|
||||||
|
|
||||||
match &typed_lval.kind {
|
if !typed_lval.is_lvalue() {
|
||||||
TypedExprKind::Identifier { .. } => {}
|
self.errors.push(SemanticError::new(
|
||||||
_ => self.errors.push(SemanticError::new(
|
|
||||||
"invalid left-hand side of assignment",
|
"invalid left-hand side of assignment",
|
||||||
lval.span,
|
lval.span,
|
||||||
)),
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = self.unify(&typed_lval.ty, &typed_rval.ty) {
|
if let Err(e) = self.unify(&typed_lval.ty, &typed_rval.ty) {
|
||||||
@@ -994,8 +1059,9 @@ impl Sema {
|
|||||||
from
|
from
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_valid = (final_from.is_numeric() || final_from == Ty::Bool)
|
let is_valid =
|
||||||
&& (to.is_numeric() || to == Ty::Bool);
|
(final_from.is_numeric() || final_from == Ty::Bool || final_from.is_pointer())
|
||||||
|
&& (to.is_numeric() || to == Ty::Bool || to.is_pointer());
|
||||||
|
|
||||||
if !is_valid {
|
if !is_valid {
|
||||||
self.errors.push(SemanticError::new(
|
self.errors.push(SemanticError::new(
|
||||||
@@ -1229,4 +1295,17 @@ mod test {
|
|||||||
let src = "fn test() { let a: f32 = 10.0; let b = a as i32; }";
|
let src = "fn test() { let a: f32 = 10.0; let b = a as i32; }";
|
||||||
assert!(analyze(src).is_ok());
|
assert!(analyze(src).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_pointer_assignment() {
|
||||||
|
let src = "fn test() { let a: i32 = 5; let b: *f32 = &a; }";
|
||||||
|
let errors = analyze(src).unwrap_err();
|
||||||
|
assert!(errors.iter().any(|e| e.message.contains("type mismatch")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_pointer_cast() {
|
||||||
|
let src = "fn test() { let a: i32 = 5; let b: *f32 = &a as *f32; }";
|
||||||
|
assert!(analyze(src).is_ok());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ pub enum TokenKind {
|
|||||||
Star,
|
Star,
|
||||||
Slash,
|
Slash,
|
||||||
Percent,
|
Percent,
|
||||||
|
Ampersand,
|
||||||
|
|
||||||
Equal,
|
Equal,
|
||||||
Unequal,
|
Unequal,
|
||||||
@@ -147,6 +148,7 @@ impl Display for TokenKind {
|
|||||||
TokenKind::Star => "`*`",
|
TokenKind::Star => "`*`",
|
||||||
TokenKind::Slash => "`/`",
|
TokenKind::Slash => "`/`",
|
||||||
TokenKind::Percent => "`%`",
|
TokenKind::Percent => "`%`",
|
||||||
|
TokenKind::Ampersand => "`&`",
|
||||||
TokenKind::Equal => "`==`",
|
TokenKind::Equal => "`==`",
|
||||||
TokenKind::Unequal => "`!=`",
|
TokenKind::Unequal => "`!=`",
|
||||||
TokenKind::LessThan => "`<`",
|
TokenKind::LessThan => "`<`",
|
||||||
|
|||||||
+51
-7
@@ -139,6 +139,7 @@ impl FuncBuilder {
|
|||||||
ty,
|
ty,
|
||||||
mutable: false,
|
mutable: false,
|
||||||
name: Some(name.clone()),
|
name: Some(name.clone()),
|
||||||
|
address_taken: false,
|
||||||
});
|
});
|
||||||
self.scopes.last_mut().unwrap().insert(name, id);
|
self.scopes.last_mut().unwrap().insert(name, id);
|
||||||
id
|
id
|
||||||
@@ -152,6 +153,7 @@ impl FuncBuilder {
|
|||||||
ty,
|
ty,
|
||||||
mutable: false,
|
mutable: false,
|
||||||
name: None,
|
name: None,
|
||||||
|
address_taken: false,
|
||||||
});
|
});
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
@@ -364,16 +366,44 @@ impl FuncBuilder {
|
|||||||
}
|
}
|
||||||
TypedExprKind::Boolean { value } => Operand::Constant(ConstantValue::Boolean(*value)),
|
TypedExprKind::Boolean { value } => Operand::Constant(ConstantValue::Boolean(*value)),
|
||||||
TypedExprKind::Unary { op, expr: inner } => {
|
TypedExprKind::Unary { op, expr: inner } => {
|
||||||
|
match op {
|
||||||
|
UnaryOp::AddressOf => match &inner.kind {
|
||||||
|
TypedExprKind::Identifier { name } => {
|
||||||
|
let id = self.lookup(name);
|
||||||
|
self.locals[id.0].address_taken = true;
|
||||||
|
let temp = self.new_temp(expr.ty.clone());
|
||||||
|
self.emit_stmt(Statement {
|
||||||
|
kind: StatementKind::Assign(temp, Rvalue::AddressOf(id)),
|
||||||
|
span: expr.span,
|
||||||
|
});
|
||||||
|
Operand::Copy(temp)
|
||||||
|
}
|
||||||
|
TypedExprKind::Unary {
|
||||||
|
op: UnaryOp::Deref,
|
||||||
|
expr: ptr_expr,
|
||||||
|
} => self.lower_expr(ptr_expr), // `&*ptr` is optimized right back to `ptr`!
|
||||||
|
_ => unreachable!("invalid lvalue for addressof"),
|
||||||
|
},
|
||||||
|
UnaryOp::Deref => {
|
||||||
|
let inner_op = self.lower_expr(inner);
|
||||||
|
let temp = self.new_temp(expr.ty.clone());
|
||||||
|
self.emit_stmt(Statement {
|
||||||
|
kind: StatementKind::Assign(temp, Rvalue::ReadPointer(inner_op)),
|
||||||
|
span: expr.span,
|
||||||
|
});
|
||||||
|
Operand::Copy(temp)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
let inner_op = self.lower_expr(inner);
|
let inner_op = self.lower_expr(inner);
|
||||||
let temp = self.new_temp(expr.ty.clone());
|
let temp = self.new_temp(expr.ty.clone());
|
||||||
|
|
||||||
self.emit_stmt(Statement {
|
self.emit_stmt(Statement {
|
||||||
kind: StatementKind::Assign(temp, Rvalue::UnaryOp(*op, inner_op)),
|
kind: StatementKind::Assign(temp, Rvalue::UnaryOp(*op, inner_op)),
|
||||||
span: expr.span,
|
span: expr.span,
|
||||||
});
|
});
|
||||||
|
|
||||||
Operand::Copy(temp)
|
Operand::Copy(temp)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
TypedExprKind::Binary { op, lhs, rhs } => {
|
TypedExprKind::Binary { op, lhs, rhs } => {
|
||||||
let lhs_op = self.lower_expr(lhs);
|
let lhs_op = self.lower_expr(lhs);
|
||||||
let rhs_op = self.lower_expr(rhs);
|
let rhs_op = self.lower_expr(rhs);
|
||||||
@@ -389,15 +419,29 @@ impl FuncBuilder {
|
|||||||
TypedExprKind::Assign { lval, rval } => {
|
TypedExprKind::Assign { lval, rval } => {
|
||||||
let rval_op = self.lower_expr(rval);
|
let rval_op = self.lower_expr(rval);
|
||||||
|
|
||||||
let local_id = match &lval.kind {
|
match &lval.kind {
|
||||||
TypedExprKind::Identifier { name } => self.lookup(name),
|
TypedExprKind::Identifier { name } => {
|
||||||
_ => panic!("invalid lval in MIR lowering"),
|
let local_id = self.lookup(name);
|
||||||
};
|
|
||||||
|
|
||||||
self.emit_stmt(Statement {
|
self.emit_stmt(Statement {
|
||||||
kind: StatementKind::Assign(local_id, Rvalue::Use(rval_op.clone())),
|
kind: StatementKind::Assign(local_id, Rvalue::Use(rval_op.clone())),
|
||||||
span: expr.span,
|
span: expr.span,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
TypedExprKind::Unary {
|
||||||
|
op: UnaryOp::Deref,
|
||||||
|
expr: ptr_expr,
|
||||||
|
} => {
|
||||||
|
let ptr_op = self.lower_expr(ptr_expr);
|
||||||
|
self.emit_stmt(Statement {
|
||||||
|
kind: StatementKind::Store {
|
||||||
|
ptr: ptr_op,
|
||||||
|
val: Rvalue::Use(rval_op.clone()),
|
||||||
|
},
|
||||||
|
span: expr.span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => unreachable!("invalid lval in MIR lowering"),
|
||||||
|
}
|
||||||
|
|
||||||
rval_op
|
rval_op
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-1
@@ -28,7 +28,9 @@ fn optimize_function(func: &mut MirFunction) {
|
|||||||
if let Some(constant) = evaluate_rvalue(rvalue) {
|
if let Some(constant) = evaluate_rvalue(rvalue) {
|
||||||
// Replace the complex instruction with a simple constant use
|
// Replace the complex instruction with a simple constant use
|
||||||
*rvalue = Rvalue::Use(Operand::Constant(constant.clone()));
|
*rvalue = Rvalue::Use(Operand::Constant(constant.clone()));
|
||||||
|
if !func.locals[local.0].address_taken {
|
||||||
known_constants.insert(*local, constant);
|
known_constants.insert(*local, constant);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Reassigned to a non-computable value; remove older cached inferences
|
// Reassigned to a non-computable value; remove older cached inferences
|
||||||
known_constants.remove(local);
|
known_constants.remove(local);
|
||||||
@@ -37,6 +39,10 @@ fn optimize_function(func: &mut MirFunction) {
|
|||||||
StatementKind::SideEffect(rvalue) => {
|
StatementKind::SideEffect(rvalue) => {
|
||||||
propagate_rvalue(rvalue, &known_constants);
|
propagate_rvalue(rvalue, &known_constants);
|
||||||
}
|
}
|
||||||
|
StatementKind::Store { ptr, val } => {
|
||||||
|
propagate_operand(ptr, &known_constants);
|
||||||
|
propagate_rvalue(val, &known_constants);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,6 +92,8 @@ fn propagate_rvalue(rvalue: &mut Rvalue, known_constants: &HashMap<LocalId, Cons
|
|||||||
propagate_operand(arg, known_constants);
|
propagate_operand(arg, known_constants);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Rvalue::AddressOf(_) => {}
|
||||||
|
Rvalue::ReadPointer(op) => propagate_operand(op, known_constants),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,7 +272,29 @@ mod test {
|
|||||||
name: "test".to_string(),
|
name: "test".to_string(),
|
||||||
params: vec![],
|
params: vec![],
|
||||||
return_type: Ty::I32,
|
return_type: Ty::I32,
|
||||||
locals: vec![],
|
locals: vec![
|
||||||
|
LocalDecl {
|
||||||
|
id: LocalId(0),
|
||||||
|
ty: Ty::I32,
|
||||||
|
mutable: false,
|
||||||
|
name: None,
|
||||||
|
address_taken: false,
|
||||||
|
},
|
||||||
|
LocalDecl {
|
||||||
|
id: LocalId(1),
|
||||||
|
ty: Ty::I32,
|
||||||
|
mutable: false,
|
||||||
|
name: None,
|
||||||
|
address_taken: false,
|
||||||
|
},
|
||||||
|
LocalDecl {
|
||||||
|
id: LocalId(2),
|
||||||
|
ty: Ty::I32,
|
||||||
|
mutable: false,
|
||||||
|
name: None,
|
||||||
|
address_taken: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
blocks: vec![BasicBlock {
|
blocks: vec![BasicBlock {
|
||||||
id: BlockId(0),
|
id: BlockId(0),
|
||||||
statements: vec![
|
statements: vec![
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ pub struct LocalDecl {
|
|||||||
pub mutable: bool,
|
pub mutable: bool,
|
||||||
/// Contains the name if it is a user-declared variable, or `None` if it's a compiler temporary.
|
/// Contains the name if it is a user-declared variable, or `None` if it's a compiler temporary.
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
|
/// Indicates if the address of this local was ever taken, forcing allocation via explicit memory stack slots instead of SSA pseudo-registers.
|
||||||
|
pub address_taken: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A sequential list of non-branching statements followed by a single terminator.
|
/// A sequential list of non-branching statements followed by a single terminator.
|
||||||
@@ -62,6 +64,8 @@ pub enum StatementKind {
|
|||||||
Assign(LocalId, Rvalue),
|
Assign(LocalId, Rvalue),
|
||||||
/// Executes an Rvalue strictly for its side effects (e.g. FFI calling `Unit` functions)
|
/// Executes an Rvalue strictly for its side effects (e.g. FFI calling `Unit` functions)
|
||||||
SideEffect(Rvalue),
|
SideEffect(Rvalue),
|
||||||
|
/// Stores a value into the memory address specified by the pointer operand.
|
||||||
|
Store { ptr: Operand, val: Rvalue },
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Operations that produce a value.
|
/// Operations that produce a value.
|
||||||
@@ -72,6 +76,8 @@ pub enum Rvalue {
|
|||||||
BinaryOp(BinaryOp, Operand, Operand),
|
BinaryOp(BinaryOp, Operand, Operand),
|
||||||
Cast(Ty, Operand),
|
Cast(Ty, Operand),
|
||||||
Call(String, Vec<Operand>, Ty),
|
Call(String, Vec<Operand>, Ty),
|
||||||
|
AddressOf(LocalId),
|
||||||
|
ReadPointer(Operand),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An atomic value used as inputs to instructions.
|
/// An atomic value used as inputs to instructions.
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[code]
|
||||||
|
fn swap(a: *i32, b: *i32) {
|
||||||
|
let temp = *a;
|
||||||
|
*a = *b;
|
||||||
|
*b = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> i32 {
|
||||||
|
let x = 10;
|
||||||
|
let y = 20;
|
||||||
|
swap(&x, &y);
|
||||||
|
return x - y; // 20 - 10 = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
[expected_return_code]
|
||||||
|
10
|
||||||
Reference in New Issue
Block a user