From dfce5af06e6556da5f7faf147161e13bc1d03a1d Mon Sep 17 00:00:00 2001 From: Zimri Leisher Date: Sat, 5 Sep 2026 14:01:04 -0400 Subject: [PATCH] Push the constant part of a dynamic store offset as a signed value so parameter stores work (#185) --- src/fpy/codegen_fpybc.py | 9 +++--- test/fpy/test_types_and_constructors.py | 37 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/fpy/codegen_fpybc.py b/src/fpy/codegen_fpybc.py index 669f699..ffdb570 100644 --- a/src/fpy/codegen_fpybc.py +++ b/src/fpy/codegen_fpybc.py @@ -1210,13 +1210,14 @@ def emit_AstAssign(self, node: AstAssign, state: CompileState): dirs.append(IntAddDirective()) # Add the constant part: base variable's frame offset + - # accumulated constant field offsets. + # accumulated constant field offsets. Signed, because a parameter + # sits below the frame start at a negative offset. const_part = base_frame_offset + field_const_offset - dirs.append(PushValDirective(FpyValue(U64, const_part).serialize())) + dirs.append(PushValDirective(FpyValue(I64, const_part).serialize())) dirs.append(IntAddDirective()) - # and now convert the u64 back into the SignedStackSizeType that store expects - dirs.extend(self.convert_numeric_type(U64, SignedStackSizeType)) + # and now convert the i64 back into the SignedStackSizeType that store expects + dirs.extend(self.convert_numeric_type(I64, SignedStackSizeType)) # now that the frame offset is pushed, use it to store into the frame if use_abs: diff --git a/test/fpy/test_types_and_constructors.py b/test/fpy/test_types_and_constructors.py index e3e047e..c8008e0 100644 --- a/test/fpy/test_types_and_constructors.py +++ b/test/fpy/test_types_and_constructors.py @@ -401,6 +401,43 @@ def pick(idx: I8) -> U32: assert pick(0) == 10 assert pick(1) == 20 +""" + assert_run_success(fprime_test_api, seq) + + def test_assign_param_array_element_runtime_index(self, fprime_test_api): + """A store through a runtime index into an array parameter. Parameters + live at negative frame offsets, and the store must land on the right + element without touching its neighbors.""" + seq = """ +def f(a: Ref.FpyExampleArray, i: I64) -> U32: + a[i] = 7 + assert a[0] == 1 + assert a[2] == 3 + return a[i] + +arr: Ref.FpyExampleArray = Ref.FpyExampleArray(1, 2, 3) +k: I64 = 1 +assert f(arr, k) == 7 +""" + assert_run_success(fprime_test_api, seq) + + def test_assign_param_array_element_member_runtime_index(self, fprime_test_api): + """A store into a struct member of a runtime-indexed element of an + array-of-struct parameter.""" + seq = """ +def set_value(pairs: Ref.SignalPairSet, i: I64, v: F32) -> F32: + pairs[i].value = v + assert pairs[i].time == 5.0 + assert pairs[1].value == 4.0 + return pairs[i].value + +p: Ref.SignalPairSet = Ref.SignalPairSet( \\ + Ref.SignalPair(1.0, 2.0), \\ + Ref.SignalPair(3.0, 4.0), \\ + Ref.SignalPair(5.0, 6.0), \\ + Ref.SignalPair(7.0, 8.0)) +idx: I64 = 2 +assert set_value(p, idx, 99.0) == 99.0 """ assert_run_success(fprime_test_api, seq)