Skip to content
Closed
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
bd7e162
executing instruction results into InstrunctionResult
RishabhRD Oct 28, 2025
79b3c0c
fix alloc and dealloc instructions
RishabhRD Oct 28, 2025
fc08a71
access instruction returns an address
RishabhRD Oct 28, 2025
8930fc0
make access ad subfield work
RishabhRD Oct 28, 2025
03fae32
register should only store builtin object
RishabhRD Oct 28, 2025
5540797
instruction also provides type
RishabhRD Oct 28, 2025
cd3312f
fix byte offsetting for subfield view
RishabhRD Oct 28, 2025
97df887
feat: implement load instruction
RishabhRD Oct 28, 2025
844594f
no need to store address additionally
RishabhRD Oct 28, 2025
644c565
feat: memory_copy implemented
RishabhRD Oct 28, 2025
e1b3b1d
feat: finally calling function works
RishabhRD Oct 28, 2025
36eb17f
feat: make jump instruction work
RishabhRD Oct 28, 2025
7d5c065
fix: unreal abi type layout calculation for bool
RishabhRD Oct 28, 2025
329a953
feat: store instruction works now
RishabhRD Oct 28, 2025
b70ed18
remove constant_string implementation as it was wrong
RishabhRD Oct 29, 2025
038a88f
Unify stack and heap addresses
RishabhRD Oct 29, 2025
1e258ed
remove unused stack tests
RishabhRD Oct 29, 2025
eb3f50d
remove #expect for checking nothrow
RishabhRD Oct 29, 2025
46f173f
Merge remote-tracking branch 'base/interpreter' into interpreter_func…
RishabhRD Oct 29, 2025
78f918b
moved stackframe to its original location
RishabhRD Oct 31, 2025
c19ee8c
memory operations is independent of stack and heap
RishabhRD Oct 31, 2025
d03877e
document the types properly
RishabhRD Oct 31, 2025
2d8b8aa
store should also get rid of little-endianess
RishabhRD Oct 31, 2025
b003955
update docs to be independent of stack and heap
RishabhRD Oct 31, 2025
605bba1
fix failing CI
RishabhRD Nov 1, 2025
3a78ffd
add relevant dependencies to cmake InterpreterTests
RishabhRD Nov 1, 2025
19ddb56
testing is also a dependency
RishabhRD Nov 1, 2025
22624ed
fix dependencies in package.swift
RishabhRD Nov 1, 2025
9119a7d
add swift-testing as dependency in cmake
RishabhRD Nov 1, 2025
875c1a6
Upgrade cmake modules for finding swift testing
tothambrus11 Nov 1, 2025
8eaaa86
disable interpreter testing target
tothambrus11 Nov 1, 2025
1e740da
remove questionable cmake commands
tothambrus11 Nov 1, 2025
cf5f430
disable cmake testing for interpreter for now
RishabhRD Nov 2, 2025
160255d
every stack allocations should be on different allocation id
RishabhRD Nov 2, 2025
f3085d5
access builtin value using accessors
RishabhRD Nov 2, 2025
5a41037
made asUInt128 initializer as fileprivate
RishabhRD Nov 2, 2025
c0e4e17
migrate to XCTest
RishabhRD Nov 4, 2025
918f632
enable interpreter tests
RishabhRD Nov 4, 2025
2e66e09
Merge branch 'interpreter' into interpreter_function_call
RishabhRD Nov 6, 2025
415c1f9
safe accessors for type stored at offset
RishabhRD Nov 7, 2025
807209d
fix: preconditions on memory tests
RishabhRD Nov 8, 2025
1a89883
added Memory.copy(bytes:,from:,to:) to support copying empty structs
RishabhRD Nov 8, 2025
bf24841
apply jumpTo name suggestion from Dave
RishabhRD Nov 8, 2025
6d1a4b6
Improve naming and documentation
RishabhRD Nov 8, 2025
9e4854c
improve using UntypedBuiltinValue
RishabhRD Nov 8, 2025
e7f854d
Merge branch 'interpreter' into interpreter_function_call
RishabhRD Nov 16, 2025
fbd247c
remove InstructionResult and let it evolve with design
RishabhRD Nov 16, 2025
ceeb73b
stackframe should contain local variables
RishabhRD Nov 16, 2025
9cfa91a
remove untyped builtin value
RishabhRD Nov 16, 2025
c2b44ab
remove the use of denotedBy
RishabhRD Nov 16, 2025
7e76b30
address of subfield in parent field
RishabhRD Nov 16, 2025
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
340 changes: 223 additions & 117 deletions Sources/Interpreter/Interpreter.swift

Large diffs are not rendered by default.

54 changes: 50 additions & 4 deletions Sources/Interpreter/Memory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,14 @@ public struct Memory {
}

mutating func withUnsafeMutablePointer<T, R>(to _: T.Type, at a: Offset, _ body: (UnsafeMutablePointer<T>)->R) -> R {
precondition(a + MemoryLayout<T>.size < size)
precondition(a + MemoryLayout<T>.size <= size)
return storage.withUnsafeMutableBytes { p in
body((p.baseAddress! + baseOffset + a).assumingMemoryBound(to: T.self))
}
}

func withUnsafePointer<T, R>(to _: T.Type, at a: Offset, _ body: (UnsafePointer<T>)->R) -> R {
precondition(a + MemoryLayout<T>.size < size)
precondition(a + MemoryLayout<T>.size <= size)
return storage.withUnsafeBytes { p in
body((p.baseAddress! + baseOffset + a).assumingMemoryBound(to: T.self))
}
Expand Down Expand Up @@ -171,7 +171,7 @@ public struct Memory {
}
}

/// Returns true if `o` is alinged to an `n` byte boundary.
/// Returns true if `o` is aligned to an `n` byte boundary.
public func offset(_ o: Offset, hasAlignment n: Int) -> Bool {
storage.withUnsafeBytes {
UInt(bitPattern: $0.baseAddress! + baseOffset + o) % UInt(n) == 0
Expand Down Expand Up @@ -257,7 +257,7 @@ public struct Memory {
try allocation[a.allocation]!.compose(t, at: a.offset)
}

/// Returns true if `a` is alinged to an `n` byte boundary.
/// Returns true if `a` is aligned to an `n` byte boundary.
public func address(_ a: Address, hasAlignment n: Int) -> Bool {
allocation[a.allocation]!.offset(a.offset, hasAlignment: n)
}
Expand All @@ -278,6 +278,33 @@ public struct Memory {
throw Error.noDecomposable(t, at: a)
}

/// 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

) {
precondition(source.offset + n <= self[source.allocation].size)
precondition(destination.offset + n <= self[destination.allocation].size)
let sourceBaseOffset = self[source.allocation].baseOffset
let destinationBaseOffset = self[destination.allocation].baseOffset
self[source.allocation].storage.withUnsafeBytes { sourceBuffer in
self[destination.allocation].storage.withUnsafeMutableBytes { destBuffer in
let s = sourceBuffer.baseAddress! + sourceBaseOffset + source.offset
let d = destBuffer.baseAddress! + destinationBaseOffset + destination.offset
d.copyMemory(from: s, byteCount: n)
}
}
}

/// The allocation identified by `i`.
public subscript(_ i: Allocation.ID) -> Allocation {
_read {
yield allocation[i]!
}
_modify {
yield &allocation[i]!
}
}

}

public extension Memory.Address {
Expand All @@ -299,3 +326,22 @@ public extension Memory.Address {
static func -=(l: inout Self, r: Int) { l = l - r }

}

extension Memory.Allocation {

/// Yields value of type `t` stored at offset `a` in allocation.
subscript<T>(_ a: Memory.Offset, type t: T.Type) -> T
{
get {
withUnsafePointer(to: t, at: a) {
$0.pointee
}
}
set {
withUnsafeMutablePointer(to: t, at: a) {
$0.pointee = newValue
}
}
}

}
5 changes: 3 additions & 2 deletions Sources/Interpreter/TargetABI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ public struct UnrealABI: TargetABI {
precondition(
bitWidth > 0 && bitWidth.nonzeroBitCount == 1,
"bit width \(bitWidth) is not a power of 2.")
let sizeInBytes = (bitWidth + 7) / 8
return .init(
alignment: min(bitWidth / 8, maxAlignment),
size: bitWidth / 8)
alignment: min(sizeInBytes, maxAlignment),
size: sizeInBytes)
}

public func layout(_ t: BuiltinType) -> TypeLayout.Bytes {
Expand Down
78 changes: 78 additions & 0 deletions Sources/Interpreter/UntypedBuiltinValue.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
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.


/// The result of reinterpretation as an unsigned integer value.
///
/// Equivalent to `UInt128(unsafeBitCast<T>(x))`, where `x` is the represented value
/// and `T` is an unsigned integer of the same size as `x`. Stores the unsigned
/// representation of integer types.
private let asUInt128: UInt128

/// The size of the value in bytes.
private let size: Int

/// Creates instance of builtin value with unsigned reinterpretation `v` and size `s`.
fileprivate init(asUInt128 v: UInt128, size s: Int) {
asUInt128 = v
size = s
}

/// Creates instance of builtin value with 8-bit integer.
static func i8(_ v: UInt8) -> Self {
Self(asUInt128: UInt128(v), size: 1)
}

/// Creates instance of builtin value with 16-bit integer.
public static func i16(_ v: UInt16) -> Self {
Self(asUInt128: UInt128(v), size: 2)
}

/// Creates instance of builtin value with 32-bit integer.
public static func i32(_ v: UInt32) -> Self {
Self(asUInt128: UInt128(v), size: 4)
}

/// Creates instance of builtin value with 64-bit integer.
public static func i64(_ v: UInt64) -> Self {
Self(asUInt128: UInt128(v), size: 8)
}

/// Creates instance of builtin value with 128-bit integer.
public static func i128(_ v: UInt128) -> Self {
Self(asUInt128: UInt128(v), size: 16)
}

/// Bool value, if present.
public var bool: Bool? {
if asUInt128 != 0 && asUInt128 != 1 { return nil }
return asUInt128 != 0
}

/// 8-bit integer value, if present.
public var i8: UInt8? { size == 1 ? UInt8(truncatingIfNeeded: asUInt128) : nil }

/// 16-bit integer value, if present
public var i16: UInt16? { size == 2 ? UInt16(truncatingIfNeeded: asUInt128) : nil }

/// 32-bit integer value, if present
public var i32: UInt32? { size == 4 ? UInt32(truncatingIfNeeded: asUInt128) : nil }

/// 64-bit integer value, if present
public var i64: UInt64? { size == 8 ? UInt64(truncatingIfNeeded: asUInt128) : nil }

/// 128-bit integer value, if present
public var i128: UInt128? { size == 16 ? asUInt128 : nil }

}

/// Methods to create builtin value from IR constants.
extension UntypedBuiltinValue {

/// Creates instance of builtin value with integer constant `c`.
public init(withIntegerConstant c: IntegerConstant) {
self.init(asUInt128: UInt128(truncatingIfNeeded: c.value), size: (c.value.bitWidth + 7) / 8)
}

}
2 changes: 1 addition & 1 deletion Tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ add_hylo_test_of(FrontEnd NAMED HyloTests
DEPENDENCIES IR TestUtils StandardLibrary Utils Algorithms)

add_hylo_test_of(Interpreter NAMED InterpreterTests
DEPENDENCIES Interpreter IR
DEPENDENCIES Interpreter FrontEnd IR TestUtils
# TODO: why is this needed?
Collections
)
Expand Down
65 changes: 64 additions & 1 deletion Tests/InterpreterTests/InterpreterTests.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Driver
import Foundation
import IR
import XCTest
Expand All @@ -21,4 +20,68 @@ final class InterpreterRunTests: XCTestCase {
}
}

func testStackAllocAndDealloc() throws {
let input =
"""
public fun main() {
let x = 2
let y = 2
}
""".asSourceFile()
let module = try input.loweredToIRAsMainWithHostedStandardLibrary();
let program = IR.Program.init(syntax: module.program, modules: [module.id: module]);
var executor = Interpreter(program);
while executor.isRunning {
try executor.step()
}
}

func testFunctionCall() throws {
let input =
"""
public fun dummy() {}

public fun id(_ x: sink Int) -> Int {
dummy()
return x
}

public fun main() {
let x = 2
let y = id(x)
let z = 3
}
""".asSourceFile()
let module = try input.loweredToIRAsMainWithHostedStandardLibrary();
let program = IR.Program.init(syntax: module.program, modules: [module.id: module]);
var executor = Interpreter(program);
while executor.isRunning {
try executor.step()
}
}

func testBranch() throws {
let input =
"""
public fun select(_ cond: Bool, _ first: sink Int, _ second: sink Int) -> Int {
if cond {
return first
}else{
return second
}
}

public fun main() {
_ = select(true, 2, 3)
_ = select(false, 2, 3)
}
""".asSourceFile()
let module = try input.loweredToIRAsMainWithHostedStandardLibrary();
let program = IR.Program.init(syntax: module.program, modules: [module.id: module]);
var executor = Interpreter(program);
while executor.isRunning {
try executor.step()
}
}

}
1 change: 1 addition & 0 deletions Tests/InterpreterTests/MemoryTests+internal.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import XCTest
import TestUtils
import FrontEnd
import XCTest
@testable import Interpreter

final class InterpreterMemoryInternalTests: XCTestCase {
Expand Down
2 changes: 2 additions & 0 deletions Tests/InterpreterTests/MemoryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import FrontEnd
import XCTest
import TestUtils
import Interpreter
import XCTest

final class InterpreterMemoryTests: XCTestCase {

Expand Down Expand Up @@ -149,4 +150,5 @@ final class InterpreterMemoryTests: XCTestCase {
try m.decompose(i16, at: p + parts[1].offset)
*/
}

}
Loading