Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/fpy/codegen_llvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@ def is_addressable(expr: AstExpr, state: CompileState) -> bool:
return is_instance_compat(sym, (VariableSymbol, FieldAccess))


def denotes_location(expr: AstExpr, state: CompileState) -> bool:
"""True when a pointer to *expr* is formed from its own location: it is
addressable and has not folded to a constant. A folded access (a constant
element of a constant constructor call) is emitted as its constant, so an
access into it copies that constant to a temp slot instead."""
return is_addressable(expr, state) and state.const_expr_values.get(expr) is None


class EmitLlvmExpr(Emitter):
"""Lowers a single Fpy arithmetic/comparison expression into LLVM IR.

Expand Down Expand Up @@ -276,7 +284,7 @@ def _emit_ptr(self, expr: AstExpr, state: CompileState) -> ir.Value:
"""Emit a pointer to *expr*'s value, without loading it."""
b = self.builder
i32 = ir.IntType(32)
if not is_addressable(expr, state):
if not denotes_location(expr, state):
return self._emit_to_temp_slot(expr, state)
sym = state.resolved_symbols[expr]
if is_instance_compat(sym, VariableSymbol):
Expand Down Expand Up @@ -983,7 +991,7 @@ def _temp_slot_parent(self, access: AstExpr, state: CompileState) -> AstExpr | N

# _emit_ptr copies its argument to a slot on exactly this condition.
parent = state.resolved_symbols[access].parent_expr
if is_addressable(parent, state):
if denotes_location(parent, state):
return None
if parent in state.backend.temp_slots:
# The AST can share one expression between two places (a default
Expand Down
22 changes: 22 additions & 0 deletions test/fpy/test_types_and_constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,28 @@ def pick(idx: I8) -> U32:

assert pick(0) == 10
assert pick(1) == 20
"""
assert_run_success(fprime_test_api, seq)

def test_get_variable_idx_of_folded_element_of_ctor_result(self, fprime_test_api):
"""A runtime index into a constant-indexed element of a constant
expression: the constant prefix folds, and the runtime index must
still find storage to index into."""
seq = """
j: I64 = 1
c: Ref.Choice = Ref.TooManyChoices( \\
Ref.ManyChoices(Ref.Choice.ONE, Ref.Choice.TWO), \\
Ref.ManyChoices(Ref.Choice.RED, Ref.Choice.BLUE))[0][j]
assert c == Ref.Choice.TWO
"""
assert_run_success(fprime_test_api, seq)

def test_get_variable_idx_of_folded_member_of_ctor_result(self, fprime_test_api):
seq = """
j: I64 = 1
x: F32 = Ref.SignalInfo(Ref.SignalType.SINE, \\
Ref.SignalSet(1.0, 2.0, 3.0, 4.0), Ref.SignalPairSet()).history[j]
assert x == 2.0
"""
assert_run_success(fprime_test_api, seq)

Expand Down
Loading