Skip to content

Commit 978a8dc

Browse files
committed
Fix Cranelift frem codegen and Grammar2 parse stack overflow
1 parent eff8b90 commit 978a8dc

3 files changed

Lines changed: 182 additions & 48 deletions

File tree

crates/compiler/src/cranelift_backend.rs

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,8 +1173,7 @@ impl CraneliftBackend {
11731173
BinaryOp::FMul => builder.ins().fmul(lhs, rhs),
11741174
BinaryOp::FDiv => builder.ins().fdiv(lhs, rhs),
11751175
BinaryOp::FRem => {
1176-
// TODO: Re-implement fmod import in multi-block context
1177-
builder.ins().f64const(0.0)
1176+
Self::call_libm_fmod(&mut self.module, &mut builder, lhs, rhs)?
11781177
}
11791178
// Float comparisons also always return bool (i8)
11801179
BinaryOp::FEq
@@ -4148,27 +4147,49 @@ impl CraneliftBackend {
41484147
}
41494148
}
41504149

4151-
/// Call libm fmod function for float remainder operation
4152-
fn call_libm_fmod(
4153-
&mut self,
4150+
/// Call libm fmod function for float remainder operation.
4151+
/// Supports scalar f32 (fmodf) and f64 (fmod); vector frem is rejected.
4152+
fn call_libm_fmod<M: Module>(
4153+
module: &mut M,
41544154
builder: &mut FunctionBuilder,
41554155
lhs: Value,
41564156
rhs: Value,
41574157
) -> CompilerResult<Value> {
4158-
// Declare fmod signature: double fmod(double x, double y)
4159-
let mut sig = self.module.make_signature();
4160-
sig.params.push(AbiParam::new(types::F64));
4161-
sig.params.push(AbiParam::new(types::F64));
4162-
sig.returns.push(AbiParam::new(types::F64));
4158+
let lhs_ty = builder.func.dfg.value_type(lhs);
4159+
let rhs_ty = builder.func.dfg.value_type(rhs);
4160+
if lhs_ty != rhs_ty {
4161+
return Err(CompilerError::CodeGen(format!(
4162+
"frem operand type mismatch: lhs={:?}, rhs={:?}",
4163+
lhs_ty, rhs_ty
4164+
)));
4165+
}
41634166

4164-
// Declare fmod as an external function
4165-
let fmod_id = self
4166-
.module
4167-
.declare_function("fmod", Linkage::Import, &sig)
4168-
.map_err(|e| CompilerError::Backend(format!("Failed to declare fmod: {}", e)))?;
4167+
let (symbol_name, param_ty) = match lhs_ty {
4168+
types::F64 => ("fmod", types::F64),
4169+
types::F32 => ("fmodf", types::F32),
4170+
other => {
4171+
return Err(CompilerError::CodeGen(format!(
4172+
"frem currently supports scalar f32/f64 only, got {:?}",
4173+
other
4174+
)));
4175+
}
4176+
};
4177+
4178+
// Declare fmod signature for the selected scalar float width.
4179+
let mut sig = module.make_signature();
4180+
sig.params.push(AbiParam::new(param_ty));
4181+
sig.params.push(AbiParam::new(param_ty));
4182+
sig.returns.push(AbiParam::new(param_ty));
4183+
4184+
// Declare fmod/fmodf as an external function.
4185+
let fmod_id = module
4186+
.declare_function(symbol_name, Linkage::Import, &sig)
4187+
.map_err(|e| {
4188+
CompilerError::Backend(format!("Failed to declare {}: {}", symbol_name, e))
4189+
})?;
41694190

41704191
// Import the function into the current function
4171-
let fmod_func = self.module.declare_func_in_func(fmod_id, builder.func);
4192+
let fmod_func = module.declare_func_in_func(fmod_id, builder.func);
41724193

41734194
// Call fmod
41744195
let call = builder.ins().call(fmod_func, &[lhs, rhs]);
@@ -4467,7 +4488,7 @@ impl CraneliftBackend {
44674488
BinaryOp::FMul => builder.ins().fmul(lhs, rhs),
44684489
BinaryOp::FDiv => builder.ins().fdiv(lhs, rhs),
44694490
// Float remainder requires libm fmod function call
4470-
BinaryOp::FRem => self.call_libm_fmod(builder, lhs, rhs)?,
4491+
BinaryOp::FRem => Self::call_libm_fmod(&mut self.module, builder, lhs, rhs)?,
44714492

44724493
// Bitwise
44734494
BinaryOp::And => builder.ins().band(lhs, rhs),
@@ -4618,7 +4639,7 @@ impl CraneliftBackend {
46184639
BinaryOp::FDiv => builder.ins().fdiv(lhs, rhs),
46194640
BinaryOp::FRem => {
46204641
// Cranelift doesn't have frem, use libm fmod
4621-
self.call_libm_fmod(builder, lhs, rhs)?
4642+
Self::call_libm_fmod(&mut self.module, builder, lhs, rhs)?
46224643
}
46234644
// Floating point comparisons - always return i8 (bool), never uextend
46244645
BinaryOp::FEq => builder.ins().fcmp(FloatCC::Equal, lhs, rhs),
@@ -6079,7 +6100,7 @@ impl CraneliftBackend {
60796100
BinaryOp::FSub => builder.ins().fsub(lhs, rhs),
60806101
BinaryOp::FMul => builder.ins().fmul(lhs, rhs),
60816102
BinaryOp::FDiv => builder.ins().fdiv(lhs, rhs),
6082-
BinaryOp::FRem => self.call_libm_fmod(builder, lhs, rhs)?,
6103+
BinaryOp::FRem => Self::call_libm_fmod(&mut self.module, builder, lhs, rhs)?,
60836104
// Float comparisons - also always return bool (i8)
60846105
BinaryOp::FEq
60856106
| BinaryOp::FNe

crates/compiler/tests/cranelift_backend_tests.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,38 @@ fn test_vector_f32x4_add_compilation() {
226226
.expect("Failed to compile f32x4 add function");
227227
}
228228

229+
/// Test scalar float remainder codegen (f64 + f32).
230+
#[test]
231+
fn test_scalar_float_remainder_compilation() {
232+
let mut backend = CraneliftBackend::new().expect("Failed to create backend");
233+
234+
let f64_func = create_float_arithmetic_function("f64_rem", BinaryOp::FRem, HirType::F64);
235+
backend
236+
.compile_function(f64_func.id, &f64_func)
237+
.expect("Failed to compile f64 remainder function");
238+
239+
let f32_func = create_float_arithmetic_function("f32_rem", BinaryOp::FRem, HirType::F32);
240+
backend
241+
.compile_function(f32_func.id, &f32_func)
242+
.expect("Failed to compile f32 remainder function");
243+
}
244+
245+
/// Vector float remainder is currently unsupported in Cranelift path.
246+
#[test]
247+
fn test_vector_float_remainder_rejected() {
248+
let mut backend = CraneliftBackend::new().expect("Failed to create backend");
249+
let func = create_vector_arithmetic_function("vec_f32x4_rem", BinaryOp::FRem, HirType::F32, 4);
250+
let err = backend
251+
.compile_function(func.id, &func)
252+
.expect_err("Expected vector frem to be rejected");
253+
let msg = err.to_string();
254+
assert!(
255+
msg.contains("frem currently supports scalar f32/f64 only"),
256+
"unexpected error: {}",
257+
msg
258+
);
259+
}
260+
229261
/// Test comparison operations
230262
#[test]
231263
fn test_comparison_operations() {
@@ -438,6 +470,57 @@ fn create_vector_arithmetic_function(
438470
func
439471
}
440472

473+
/// Helper function to create scalar float arithmetic operations.
474+
fn create_float_arithmetic_function(name: &str, op: BinaryOp, ty: HirType) -> HirFunction {
475+
let name = create_test_string(name);
476+
477+
let sig = HirFunctionSignature {
478+
params: vec![
479+
HirParam {
480+
id: HirId::new(),
481+
name: create_test_string("a"),
482+
ty: ty.clone(),
483+
attributes: ParamAttributes::default(),
484+
},
485+
HirParam {
486+
id: HirId::new(),
487+
name: create_test_string("b"),
488+
ty: ty.clone(),
489+
attributes: ParamAttributes::default(),
490+
},
491+
],
492+
returns: vec![ty.clone()],
493+
type_params: vec![],
494+
const_params: vec![],
495+
lifetime_params: vec![],
496+
is_variadic: false,
497+
is_async: false,
498+
effects: vec![],
499+
is_pure: false,
500+
};
501+
502+
let mut func = HirFunction::new(name, sig);
503+
let entry_block_id = func.entry_block;
504+
let param_a = func.create_value(ty.clone(), HirValueKind::Parameter(0));
505+
let param_b = func.create_value(ty.clone(), HirValueKind::Parameter(1));
506+
let result = func.create_value(ty.clone(), HirValueKind::Instruction);
507+
let inst = HirInstruction::Binary {
508+
op,
509+
result,
510+
ty,
511+
left: param_a,
512+
right: param_b,
513+
};
514+
515+
let block = func.blocks.get_mut(&entry_block_id).unwrap();
516+
block.add_instruction(inst);
517+
block.set_terminator(HirTerminator::Return {
518+
values: vec![result],
519+
});
520+
521+
func
522+
}
523+
441524
/// Create a function with control flow (if-else)
442525
fn create_control_flow_function() -> HirFunction {
443526
let name = create_test_string("abs");

crates/zyntax_embed/src/grammar2.rs

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -113,35 +113,65 @@ impl Grammar2 {
113113
) -> Grammar2Result<TypedProgram> {
114114
use zyntax_typed_ast::source::SourceFile;
115115

116-
let interpreter = GrammarInterpreter::new(&self.grammar);
117-
118-
let mut builder = TypedASTBuilder::new();
119-
let mut registry = TypeRegistry::new();
120-
let mut state = ParserState::new(source, &mut builder, &mut registry);
121-
122-
// Parse from the entry rule
123-
let result = interpreter.parse(&mut state);
124-
125-
match result {
126-
ParseResult::Success(ParsedValue::Program(mut program), _) => {
127-
// Add source file for diagnostics
128-
program.source_files =
129-
vec![SourceFile::new(filename.to_string(), source.to_string())];
130-
Ok(*program)
131-
}
132-
ParseResult::Success(other, _) => {
133-
// If we get something other than a program, wrap it
134-
eprintln!(
135-
"[Grammar2] Warning: parse returned {:?}, expected Program",
136-
std::mem::discriminant(&other)
137-
);
138-
Err(Grammar2Error::UnexpectedResult)
139-
}
140-
ParseResult::Failure(e) => Err(Grammar2Error::SourceParseError(format!(
141-
"Parse error at {}:{}: expected {:?}",
142-
e.line, e.column, e.expected
143-
))),
144-
}
116+
const PARSE_STACK_SIZE_BYTES: usize = 64 * 1024 * 1024;
117+
118+
let grammar = Arc::clone(&self.grammar);
119+
let source_owned = source.to_string();
120+
let filename_owned = filename.to_string();
121+
122+
let handle = std::thread::Builder::new()
123+
.name("grammar2-parse".to_string())
124+
.stack_size(PARSE_STACK_SIZE_BYTES)
125+
.spawn(move || {
126+
let interpreter = GrammarInterpreter::new(&grammar);
127+
128+
let mut builder = TypedASTBuilder::new();
129+
let mut registry = TypeRegistry::new();
130+
let mut state = ParserState::new(&source_owned, &mut builder, &mut registry);
131+
132+
// Parse from the entry rule
133+
let result = interpreter.parse(&mut state);
134+
135+
match result {
136+
ParseResult::Success(ParsedValue::Program(mut program), _) => {
137+
// Add source file for diagnostics
138+
program.source_files = vec![SourceFile::new(
139+
filename_owned.clone(),
140+
source_owned.clone(),
141+
)];
142+
Ok(*program)
143+
}
144+
ParseResult::Success(other, _) => {
145+
// If we get something other than a program, wrap it
146+
eprintln!(
147+
"[Grammar2] Warning: parse returned {:?}, expected Program",
148+
std::mem::discriminant(&other)
149+
);
150+
Err(Grammar2Error::UnexpectedResult)
151+
}
152+
ParseResult::Failure(e) => Err(Grammar2Error::SourceParseError(format!(
153+
"Parse error at {}:{}: expected {:?}",
154+
e.line, e.column, e.expected
155+
))),
156+
}
157+
})
158+
.map_err(|e| {
159+
Grammar2Error::SourceParseError(format!("Failed to spawn parser thread: {}", e))
160+
})?;
161+
162+
handle.join().map_err(|panic_payload| {
163+
let panic_msg = if let Some(s) = panic_payload.downcast_ref::<&str>() {
164+
(*s).to_string()
165+
} else if let Some(s) = panic_payload.downcast_ref::<String>() {
166+
s.clone()
167+
} else {
168+
"unknown panic payload".to_string()
169+
};
170+
Grammar2Error::SourceParseError(format!(
171+
"Grammar2 parser thread panicked: {}",
172+
panic_msg
173+
))
174+
})?
145175
}
146176

147177
/// Parse source code with plugin signatures (for proper extern declarations)

0 commit comments

Comments
 (0)