Skip to content

Conversation

@RishabhRD
Copy link
Member

@RishabhRD RishabhRD commented Oct 30, 2025

Design Changes

  • Introduced notion of function parameters as it was part of IR.
  • Unify the memory representation of stack and heap objects.
  • Instructions results in InstructionResult type instead of Any upon execution.

Unified Memory Representation

  • Pointer instructions require conversion from pointer to address and vice-versa.
  • For enabling this mechanism, there should be some way to encode a memory address as pointer.
  • With UUID used in stack addressing, this mechanism is not possible without overhead of huge storage as UUID itself is 128 bits and pointer on any machine can be maximum 64 bits. And for supporting any generic target ABI, this is not the way.
  • Also different address for stack and heap objects would waste a discriminator bit too.
  • Hence, we need to unify the way to address stack and heap objects.

Design

  • Stack is also a memory allocation now that just stores local variables.
  • Stack now has fixed memory. Stack gets created on initialization of interpreter.
  • StackFrame is now just responsible for registers, return address and parameters. There are classic techiniques which can also make returnAddress and parameters part of stack. I am not sure if we want that for now.
  • Because stack is also a memory allocation now, addresses for heap objects and stack objects are of same type and will be useful for implementing pointer operations.

Instructions implemented:

  1. Access
  2. AllocStack
  3. Branch
  4. Call
  5. CondBranch
  6. DeallocStack
  7. Load
  8. MemoryCopy
  9. Store [Partial support for only integer types]
  10. SubfieldView
  11. Load

Instructions UN-implemented

  1. ConstantString

@RishabhRD RishabhRD changed the title Interpreter: Make function calls working Interpreter: Make function calls and basic instructions working Oct 30, 2025
@RishabhRD RishabhRD requested a review from dabrahams October 30, 2025 10:16

}

struct ObjectAddress {
Copy link
Collaborator

@dabrahams dabrahams Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be called “Pointer?” I don't think “Object” is adding anything.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are instructions like AddressToPointer, thus I didn't pick the name Pointer. But yeah, "object" can be removed from name.

Copy link
Collaborator

@dabrahams dabrahams left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've only just started with the review, but I must say it's hard to evaluate these diffs, because entities like StackFrame do not show up in the same place.

@RishabhRD
Copy link
Member Author

I've only just started with the review, but I must say it's hard to evaluate these diffs, because entities like StackFrame do not show up in the same place.

Sorry! I accidentally moved stackframe down, fixed that. I should have observed that while raising PR itself.

}

/// UInt8 value, if present.
public var uint8: UInt8? { size == 1 ? UInt8(truncatingIfNeeded: asUInt128) : nil }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public var uint8: UInt8? { size == 1 ? UInt8(truncatingIfNeeded: asUInt128) : nil }
public var i8: UInt8? { size == 1 ? UInt8(truncatingIfNeeded: asUInt128) : nil }

Because that's what we call it in instructions

import IR

/// The value of a `Builtin` type instance, stripped of type information.
struct UntypedBuiltinValue {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And now I realize this doesn't represent builtin ptr types I think? IIRC it was my intention that it would; we'd just encode it into the UInt128…

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UntypedBuiltinValue should represent builtin ptr. But encoding to UInt128 might not be possible. As we could have instruction like this:

  %i0.0: &[{}] (sink Int) let -> Int = alloc_stack [{}] (sink Int) let -> Int
  %i0.1: ptr = address_to_pointer @FunctionDecl(524290)
  %i0.2: &ptr = subfield_view %i0.0, 0
  %i0.3: &ptr = access [set] %i0.2
  store %i0.1, %i0.3
  end_access %i0.3

Here thing to note is:

  • Address should also be able to represent function id.
  • We need to support "function pointers" too.

IDK if Function.ID can be represented in 128 bits.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think Address should represent function IDs. However, the need to represent capture-less lambdas is real and leaves me more and more convinced that we shouldn't try and stuff different types in here.

if !a.memoryLayout.type.isBuiltin {
return nil
}
let allocIdx = a.memoryAddress.allocation
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did this name come from? We don't use abbrevs. Also it's all type information at an even lower level of abstraction than Allocation.ID which is already too low. What role is the value playing in this context? That's what goes in the name. If you can't do better than to reflect type information, use a single-character name like a.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah, this is really a bad habit, mostly coming from work related codebases which normalize the same 😞. Trying to improve on it now!!!

Comment on lines 352 to 353
/// Returns address of subfield denoted by `path` stored in field at `a`.
mutating func subField(denotedBy path: RecordPath, of a: Address)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

denotedBy is a lot of big words with little semantic value. path is just type information.

Suggested change
/// Returns address of subfield denoted by `path` stored in field at `a`.
mutating func subField(denotedBy path: RecordPath, of a: Address)
/// Returns the address of `subField` in the object at `origin`.
mutating func address(of subField: RecordPath, in origin: Address)

My suggestion is imperfect and should eventually be improved. The main reason is that I don't think we know how to talk about or properly name the thing called Address here. There may not be an object there yet. I wonder if it should be called ValueStorage, i.e. the storage for a value.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding ValueStorage, knowing that Function.ID is also an Address (coming from IR instruction), I am not sure if ValueStorage would be good.

Also, shouldn't we use at instead of in in function name? Then it would match doc summary too.


/// Copies `n` bytes from `source` to `destination`.
public mutating func copy(
bytes n: Int, from source: Memory.Address, to destination: Memory.Address
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bytes n: Int, from source: Memory.Address, to destination: Memory.Address
byteCount n: Int, from source: Memory.Address, to destination: Memory.Address

n is not the bytes. However, you could:

Suggested change
bytes n: Int, from source: Memory.Address, to destination: Memory.Address
_ n: Int, bytesFrom source: Memory.Address, to destination: Memory.Address

_ = x
case let x as Load:
_ = x
let address = address(denotedBy: x.source)!;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This denotedBy label isn't working for me.

Comment on lines 327 to 328
/// Returns address of object denoted by operand, if any.
func address(denotedBy operand: Operand) -> Address? {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it the address of the object denoted by the operand, or the address denoted by the operand? The name is not consistent with the contract.

Isn't this just the operand, which must denote an address?

}

/// The value produced by executing an instruction.
enum InstructionResult {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a named type was a good idea. Maybe just an alias?

Comment on lines +315 to +316
/// Returns builtin value referenced by `operand`, if any.
func toBuiltinValue(_ operand: Operand) -> BuiltinValue? {
Copy link
Collaborator

@dabrahams dabrahams Nov 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't repeat type information in the names of non-types

Suggested change
/// Returns builtin value referenced by `operand`, if any.
func toBuiltinValue(_ operand: Operand) -> BuiltinValue? {
/// Returns the value of `x` if it has a builtin type, or `nil` if it does not.
func builtinValue(_ x: Operand) -> BuiltinValue? {

)
}

/// Store builtin value `v` at address `a`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Store builtin value `v` at address `a`.
/// Stores `v` in memory at `a`.

Comment on lines +385 to +386
/// Moves the program counter to start of `b`.
mutating func jumpTo(_ b: Block.ID) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Moves the program counter to start of `b`.
mutating func jumpTo(_ b: Block.ID) {
/// Sets the program counter to the start of `b`.
mutating func jump(to b: Block.ID) {

Read the swift API guidelines regarding prepositions.

Comment on lines +368 to +369
/// Returns block parameters from operands in `arg`.
func blockParams(from arg: Call) -> [Address] {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the comment. It probably should not require me to know what block parameters are, since that is the name of the function, except for the abbrev, which you should nix.

I think it returns the addresses of arguments followed by that of the return value space for arg.

arg is a terrible name for a function parameter; of course you're going to pass an argument. Another abbrev. Is it not notionally a function call?

Copy link
Collaborator

@dabrahams dabrahams left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get the sense that perhaps this PR is trying to do too much all at once. Partly I think so because I find myself wanting to make too many corrections.
Would it make sense to break it into smaller units, so we can make a really clean PR and learn from that?

@RishabhRD
Copy link
Member Author

Closing this PR as it is broken into #1775, #1780, #1781, #1782, #1783, #1784 as suggested.

@RishabhRD RishabhRD closed this Nov 29, 2025
@codecov
Copy link

codecov bot commented Nov 29, 2025

Codecov Report

❌ Patch coverage is 86.55914% with 25 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.14%. Comparing base (46d7e3f) to head (7e76b30).

Files with missing lines Patch % Lines
Sources/Interpreter/BuiltinValue.swift 41.17% 20 Missing ⚠️
Sources/Interpreter/Interpreter.swift 96.06% 5 Missing ⚠️
Additional details and impacted files
@@               Coverage Diff               @@
##           interpreter    #1753      +/-   ##
===============================================
+ Coverage        87.85%   88.14%   +0.29%     
===============================================
  Files              392      393       +1     
  Lines            24618    24734     +116     
===============================================
+ Hits             21627    21803     +176     
+ Misses            2991     2931      -60     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@RishabhRD RishabhRD deleted the interpreter_function_call branch November 29, 2025 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants