Preserve narrow VALUE call widths
- SHA
9989fa5f69b0e4a58b345099f882380bb21f43e4- Parents
-
ea1a9b7 - Tree
c2e66d0
9989fa5
9989fa5f69b0e4a58b345099f882380bb21f43e4ea1a9b7
c2e66d0| Status | File | + | - |
|---|---|---|---|
| M |
src/ir/lower.rs
|
35 | 9 |
| M |
tests/calling_convention_runtime.rs
|
82 | 0 |
src/ir/lower.rsmodified@@ -8374,6 +8374,24 @@ fn callee_arg_symbol<'a>( | ||
| 8374 | 8374 | scope.symbols.get(arg_name) |
| 8375 | 8375 | } |
| 8376 | 8376 | |
| 8377 | +fn callee_arg_ir_type(st: &SymbolTable, callee_name: &str, idx: usize) -> Option<IrType> { | |
| 8378 | + callee_arg_symbol(st, callee_name, idx) | |
| 8379 | + .and_then(|sym| sym.type_info.as_ref()) | |
| 8380 | + .map(type_info_to_ir_type) | |
| 8381 | +} | |
| 8382 | + | |
| 8383 | +fn coerce_value_call_arg( | |
| 8384 | + b: &mut FuncBuilder, | |
| 8385 | + st: &SymbolTable, | |
| 8386 | + callee_name: &str, | |
| 8387 | + idx: usize, | |
| 8388 | + raw: ValueId, | |
| 8389 | +) -> ValueId { | |
| 8390 | + callee_arg_ir_type(st, callee_name, idx) | |
| 8391 | + .map(|ty| coerce_to_type(b, raw, &ty)) | |
| 8392 | + .unwrap_or(raw) | |
| 8393 | +} | |
| 8394 | + | |
| 8377 | 8395 | fn zero_value_for_ir_type(b: &mut FuncBuilder, ty: &IrType) -> ValueId { |
| 8378 | 8396 | match ty { |
| 8379 | 8397 | IrType::Int(IntWidth::I64) => b.const_i64(0), |
@@ -8399,9 +8417,7 @@ fn missing_optional_call_arg( | ||
| 8399 | 8417 | if !is_value { |
| 8400 | 8418 | return b.const_i64(0); |
| 8401 | 8419 | } |
| 8402 | - callee_arg_symbol(st, callee_name, idx) | |
| 8403 | - .and_then(|sym| sym.type_info.as_ref()) | |
| 8404 | - .map(type_info_to_ir_type) | |
| 8420 | + callee_arg_ir_type(st, callee_name, idx) | |
| 8405 | 8421 | .map(|ty| zero_value_for_ir_type(b, &ty)) |
| 8406 | 8422 | .unwrap_or_else(|| b.const_i32(0)) |
| 8407 | 8423 | } |
@@ -8726,7 +8742,7 @@ fn emit_named_function_call( | ||
| 8726 | 8742 | descriptor_params, |
| 8727 | 8743 | ) |
| 8728 | 8744 | } else if is_value { |
| 8729 | - lower_expr_full( | |
| 8745 | + let raw = lower_expr_full( | |
| 8730 | 8746 | b, |
| 8731 | 8747 | locals, |
| 8732 | 8748 | e, |
@@ -8735,7 +8751,8 @@ fn emit_named_function_call( | ||
| 8735 | 8751 | internal_funcs, |
| 8736 | 8752 | contained_host_refs, |
| 8737 | 8753 | descriptor_params, |
| 8738 | - ) | |
| 8754 | + ); | |
| 8755 | + coerce_value_call_arg(b, st, abi_primary_key, i, raw) | |
| 8739 | 8756 | } else if wants_descriptor { |
| 8740 | 8757 | lower_arg_descriptor(b, locals, e, st, type_layouts) |
| 8741 | 8758 | } else if wants_string_descriptor { |
@@ -8982,7 +8999,8 @@ fn lower_alloc_return_call_into_desc( | ||
| 8982 | 8999 | Some(ctx.descriptor_params), |
| 8983 | 9000 | ) |
| 8984 | 9001 | } else if is_value { |
| 8985 | - lower_expr_ctx(b, ctx, e) | |
| 9002 | + let raw = lower_expr_ctx(b, ctx, e); | |
| 9003 | + coerce_value_call_arg(b, ctx.st, abi_primary_key, i, raw) | |
| 8986 | 9004 | } else if wants_string_descriptor { |
| 8987 | 9005 | lower_arg_string_descriptor(b, &ctx.locals, e, ctx.st, Some(ctx.type_layouts)) |
| 8988 | 9006 | } else if wants_descriptor { |
@@ -12674,7 +12692,7 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { | ||
| 12674 | 12692 | Some(ctx.descriptor_params), |
| 12675 | 12693 | ) |
| 12676 | 12694 | } else if is_value { |
| 12677 | - lower_expr_full( | |
| 12695 | + let raw = lower_expr_full( | |
| 12678 | 12696 | b, |
| 12679 | 12697 | &ctx.locals, |
| 12680 | 12698 | e, |
@@ -12683,6 +12701,13 @@ fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &SpannedStmt) { | ||
| 12683 | 12701 | Some(ctx.internal_funcs), |
| 12684 | 12702 | Some(ctx.contained_host_refs), |
| 12685 | 12703 | Some(ctx.descriptor_params), |
| 12704 | + ); | |
| 12705 | + coerce_value_call_arg( | |
| 12706 | + b, | |
| 12707 | + ctx.st, | |
| 12708 | + abi_primary_key, | |
| 12709 | + i, | |
| 12710 | + raw, | |
| 12686 | 12711 | ) |
| 12687 | 12712 | } else if wants_descriptor { |
| 12688 | 12713 | lower_arg_descriptor( |
@@ -23381,7 +23406,7 @@ fn lower_expr_full( | ||
| 23381 | 23406 | descriptor_params, |
| 23382 | 23407 | ) |
| 23383 | 23408 | } else if is_value { |
| 23384 | - lower_expr_full( | |
| 23409 | + let raw = lower_expr_full( | |
| 23385 | 23410 | b, |
| 23386 | 23411 | locals, |
| 23387 | 23412 | e, |
@@ -23390,7 +23415,8 @@ fn lower_expr_full( | ||
| 23390 | 23415 | internal_funcs, |
| 23391 | 23416 | contained_host_refs, |
| 23392 | 23417 | descriptor_params, |
| 23393 | - ) | |
| 23418 | + ); | |
| 23419 | + coerce_value_call_arg(b, st, abi_primary_key, i, raw) | |
| 23394 | 23420 | } else if wants_descriptor { |
| 23395 | 23421 | lower_arg_descriptor(b, locals, e, st, type_layouts) |
| 23396 | 23422 | } else if wants_string_descriptor { |
tests/calling_convention_runtime.rsmodified@@ -231,6 +231,88 @@ fn bind_c_ninth_float_arg_spills_with_integer_args_still_in_registers() { | ||
| 231 | 231 | let _ = std::fs::remove_dir_all(&dir); |
| 232 | 232 | } |
| 233 | 233 | |
| 234 | +#[test] | |
| 235 | +fn bind_c_signed_char_value_args_keep_narrow_stack_widths() { | |
| 236 | + let dir = unique_dir("i8_stack"); | |
| 237 | + let c_src = write_program_in( | |
| 238 | + &dir, | |
| 239 | + "check_i8_stack.c", | |
| 240 | + "#include <stdint.h>\n\nint check_i8_stack(int8_t a1, int8_t a2, int8_t a3, int8_t a4, int8_t a5, int8_t a6, int8_t a7, int8_t a8, int8_t a9, int8_t a10) {\n if (a1 != 1) return 1;\n if (a2 != 2) return 2;\n if (a3 != 3) return 3;\n if (a4 != 4) return 4;\n if (a5 != 5) return 5;\n if (a6 != 6) return 6;\n if (a7 != 7) return 7;\n if (a8 != 8) return 8;\n if (a9 != 9) return 9;\n if (a10 != 10) return 10;\n return 19;\n}\n", | |
| 241 | + ); | |
| 242 | + let c_obj = dir.join("check_i8_stack.o"); | |
| 243 | + compile_c_object(&c_src, &c_obj); | |
| 244 | + | |
| 245 | + let f_src = write_program_in( | |
| 246 | + &dir, | |
| 247 | + "main.f90", | |
| 248 | + "program p\n use iso_c_binding, only: c_int, c_signed_char\n implicit none\n interface\n function check_i8_stack(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) result(rc) bind(C, name='check_i8_stack')\n import :: c_int, c_signed_char\n integer(c_signed_char), value :: a1, a2, a3, a4, a5, a6, a7, a8, a9, a10\n integer(c_int) :: rc\n end function check_i8_stack\n end interface\n integer(c_int) :: rc\n\n rc = check_i8_stack(1_c_signed_char, 2_c_signed_char, 3_c_signed_char, 4_c_signed_char, 5_c_signed_char, 6_c_signed_char, 7_c_signed_char, 8_c_signed_char, 9_c_signed_char, 10_c_signed_char)\n if (rc /= 19_c_int) error stop rc\n print *, 'ok'\nend program\n", | |
| 249 | + ); | |
| 250 | + let f_obj = dir.join("main.o"); | |
| 251 | + compile_fortran_object(&f_src, &f_obj); | |
| 252 | + | |
| 253 | + let exe = dir.join("i8_stack.bin"); | |
| 254 | + link_program(&[&f_obj, &c_obj], &exe); | |
| 255 | + | |
| 256 | + let run = Command::new(&exe) | |
| 257 | + .output() | |
| 258 | + .expect("c_signed_char stack runtime failed"); | |
| 259 | + assert!( | |
| 260 | + run.status.success(), | |
| 261 | + "c_signed_char stack runtime failed: status={:?}\nstdout:\n{}\nstderr:\n{}", | |
| 262 | + run.status, | |
| 263 | + String::from_utf8_lossy(&run.stdout), | |
| 264 | + String::from_utf8_lossy(&run.stderr) | |
| 265 | + ); | |
| 266 | + assert!( | |
| 267 | + String::from_utf8_lossy(&run.stdout).contains("ok"), | |
| 268 | + "unexpected c_signed_char stack output: {}", | |
| 269 | + String::from_utf8_lossy(&run.stdout) | |
| 270 | + ); | |
| 271 | + | |
| 272 | + let _ = std::fs::remove_dir_all(&dir); | |
| 273 | +} | |
| 274 | + | |
| 275 | +#[test] | |
| 276 | +fn bind_c_short_value_args_keep_narrow_stack_widths() { | |
| 277 | + let dir = unique_dir("i16_stack"); | |
| 278 | + let c_src = write_program_in( | |
| 279 | + &dir, | |
| 280 | + "check_i16_stack.c", | |
| 281 | + "#include <stdint.h>\n\nint check_i16_stack(int16_t a1, int16_t a2, int16_t a3, int16_t a4, int16_t a5, int16_t a6, int16_t a7, int16_t a8, int16_t a9, int16_t a10) {\n if (a1 != 1) return 1;\n if (a2 != 2) return 2;\n if (a3 != 3) return 3;\n if (a4 != 4) return 4;\n if (a5 != 5) return 5;\n if (a6 != 6) return 6;\n if (a7 != 7) return 7;\n if (a8 != 8) return 8;\n if (a9 != 9) return 9;\n if (a10 != 10) return 10;\n return 19;\n}\n", | |
| 282 | + ); | |
| 283 | + let c_obj = dir.join("check_i16_stack.o"); | |
| 284 | + compile_c_object(&c_src, &c_obj); | |
| 285 | + | |
| 286 | + let f_src = write_program_in( | |
| 287 | + &dir, | |
| 288 | + "main.f90", | |
| 289 | + "program p\n use iso_c_binding, only: c_int, c_short\n implicit none\n interface\n function check_i16_stack(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) result(rc) bind(C, name='check_i16_stack')\n import :: c_int, c_short\n integer(c_short), value :: a1, a2, a3, a4, a5, a6, a7, a8, a9, a10\n integer(c_int) :: rc\n end function check_i16_stack\n end interface\n integer(c_int) :: rc\n\n rc = check_i16_stack(1_c_short, 2_c_short, 3_c_short, 4_c_short, 5_c_short, 6_c_short, 7_c_short, 8_c_short, 9_c_short, 10_c_short)\n if (rc /= 19_c_int) error stop rc\n print *, 'ok'\nend program\n", | |
| 290 | + ); | |
| 291 | + let f_obj = dir.join("main.o"); | |
| 292 | + compile_fortran_object(&f_src, &f_obj); | |
| 293 | + | |
| 294 | + let exe = dir.join("i16_stack.bin"); | |
| 295 | + link_program(&[&f_obj, &c_obj], &exe); | |
| 296 | + | |
| 297 | + let run = Command::new(&exe) | |
| 298 | + .output() | |
| 299 | + .expect("c_short stack runtime failed"); | |
| 300 | + assert!( | |
| 301 | + run.status.success(), | |
| 302 | + "c_short stack runtime failed: status={:?}\nstdout:\n{}\nstderr:\n{}", | |
| 303 | + run.status, | |
| 304 | + String::from_utf8_lossy(&run.stdout), | |
| 305 | + String::from_utf8_lossy(&run.stderr) | |
| 306 | + ); | |
| 307 | + assert!( | |
| 308 | + String::from_utf8_lossy(&run.stdout).contains("ok"), | |
| 309 | + "unexpected c_short stack output: {}", | |
| 310 | + String::from_utf8_lossy(&run.stdout) | |
| 311 | + ); | |
| 312 | + | |
| 313 | + let _ = std::fs::remove_dir_all(&dir); | |
| 314 | +} | |
| 315 | + | |
| 234 | 316 | #[test] |
| 235 | 317 | fn contained_hidden_result_optional_gap_preserves_host_and_char_ordering() { |
| 236 | 318 | let dir = unique_dir("contained_hidden_result_gap"); |