From af3d9b4c7399bf540e3733b9117ff72439bef935 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 11:37:18 +0200 Subject: [PATCH 01/13] Refactor the representation of bundle references --- Sources/IR/Emitter.swift | 8 ++--- .../Operands/Constant/BundleReference.swift | 30 +++++++++++++++++++ .../Constant/SubscriptBundleReference.swift | 30 ------------------- .../Operands/Instruction/ProjectBundle.swift | 6 ++-- 4 files changed, 37 insertions(+), 37 deletions(-) create mode 100644 Sources/IR/Operands/Constant/BundleReference.swift delete mode 100644 Sources/IR/Operands/Constant/SubscriptBundleReference.swift diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index bf746b5eb..8805a3b33 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -1655,7 +1655,7 @@ struct Emitter { private mutating func emit( subscriptCallee callee: AnyExprID - ) -> (callee: SubscriptBundleReference, captures: [Operand]) { + ) -> (callee: BundleReference, captures: [Operand]) { // TODO: Handle captures switch callee.kind { case NameExpr.self: @@ -1672,7 +1672,7 @@ struct Emitter { private mutating func emit( namedSubscriptCallee callee: NameExpr.ID - ) -> (callee: SubscriptBundleReference, captures: [Operand]) { + ) -> (callee: BundleReference, captures: [Operand]) { switch program[callee].referredDecl { case .direct(let d, let a) where d.kind == SubscriptDecl.self: // Callee is a direct reference to a subscript declaration. @@ -1681,12 +1681,12 @@ struct Emitter { UNIMPLEMENTED() } - let b = SubscriptBundleReference(to: SubscriptDecl.ID(d)!, parameterizedBy: a) + let b = BundleReference(to: SubscriptDecl.ID(d)!, parameterizedBy: a) return (b, []) case .member(let d, let a, let s) where d.kind == SubscriptDecl.self: // Callee is a member reference to a subscript declaration. - let b = SubscriptBundleReference(to: SubscriptDecl.ID(d)!, parameterizedBy: a) + let b = BundleReference(to: SubscriptDecl.ID(d)!, parameterizedBy: a) // The callee's receiver is the sole capture. let receiver = emitLValue(receiver: s, at: ast[callee].site) diff --git a/Sources/IR/Operands/Constant/BundleReference.swift b/Sources/IR/Operands/Constant/BundleReference.swift new file mode 100644 index 000000000..18fd31cbb --- /dev/null +++ b/Sources/IR/Operands/Constant/BundleReference.swift @@ -0,0 +1,30 @@ +import Core + +/// A Hylo IR reference to a method or subscript bundle. +public struct BundleReference: Hashable { + + /// The ID of the referred bundle. + public let bundle: T.ID + + /// If `bundle` is generic, the arguments to its generic parameter. + public let arguments: GenericArguments + + /// Creates a reference to `s` parameterized by `a`. + public init(to s: T.ID, parameterizedBy a: GenericArguments) { + self.bundle = s + self.arguments = a + } + +} + +extension BundleReference: CustomStringConvertible { + + public var description: String { + if arguments.isEmpty { + return "@\(bundle)" + } else { + return "@\(bundle)<\(list: arguments.values)>" + } + } + +} diff --git a/Sources/IR/Operands/Constant/SubscriptBundleReference.swift b/Sources/IR/Operands/Constant/SubscriptBundleReference.swift deleted file mode 100644 index edfb15bc2..000000000 --- a/Sources/IR/Operands/Constant/SubscriptBundleReference.swift +++ /dev/null @@ -1,30 +0,0 @@ -import Core - -/// A Hylo IR reference to a user subscript bundle. -public struct SubscriptBundleReference: Hashable { - - /// The ID of the referred IR subscript bundle. - public let bundle: SubscriptDecl.ID - - /// If `function` is generic, the arguments to its generic parameter. - public let arguments: GenericArguments - - /// Creates a reference to `s` parameterized by `a`. - public init(to s: SubscriptDecl.ID, parameterizedBy a: GenericArguments) { - self.bundle = s - self.arguments = a - } - -} - -extension SubscriptBundleReference: CustomStringConvertible { - - public var description: String { - if arguments.isEmpty { - return "@\(bundle)" - } else { - return "@\(bundle)<\(list: arguments.values)>" - } - } - -} diff --git a/Sources/IR/Operands/Instruction/ProjectBundle.swift b/Sources/IR/Operands/Instruction/ProjectBundle.swift index e7786e1f2..ebe53262b 100644 --- a/Sources/IR/Operands/Instruction/ProjectBundle.swift +++ b/Sources/IR/Operands/Instruction/ProjectBundle.swift @@ -4,7 +4,7 @@ import Core public struct ProjectBundle: Instruction { /// The subscript bundle implementing the projections. - public let bundle: SubscriptBundleReference + public let bundle: BundleReference /// The pure functional type of the callee. public let pureCalleeType: LambdaType @@ -23,7 +23,7 @@ public struct ProjectBundle: Instruction { /// Creates an instance with the given properties. fileprivate init( - bundle: SubscriptBundleReference, + bundle: BundleReference, pureCalleeType: LambdaType, variants: [AccessEffect: Function.ID], operands: [Operand], @@ -83,7 +83,7 @@ extension Module { /// - Requires: `bundleType` is canonical and `variants` is not empty. func makeProjectBundle( applying variants: [AccessEffect: Function.ID], - of bundle: SubscriptBundleReference, + of bundle: BundleReference, typed bundleType: SubscriptType, to arguments: [Operand], at site: SourceRange From 4cab645a2f6135ff8c4f4b5057690ef6536c55a6 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 11:56:08 +0200 Subject: [PATCH 02/13] Make 'Module.isBorrowSet(_:)' internal --- Sources/IR/Module.swift | 9 +++++++++ Sources/IR/Operands/Instruction/Call.swift | 9 --------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Sources/IR/Module.swift b/Sources/IR/Module.swift index f99bfe9f3..1178a517b 100644 --- a/Sources/IR/Module.swift +++ b/Sources/IR/Module.swift @@ -791,4 +791,13 @@ public struct Module { } } + /// Returns `true` iff `o` is an `access [set]` instruction. + func isBorrowSet(_ o: Operand) -> Bool { + guard + let i = o.instruction, + let s = self[i] as? Access + else { return false } + return s.capabilities == [.set] + } + } diff --git a/Sources/IR/Operands/Instruction/Call.swift b/Sources/IR/Operands/Instruction/Call.swift index 2e572481e..f998aa06b 100644 --- a/Sources/IR/Operands/Instruction/Call.swift +++ b/Sources/IR/Operands/Instruction/Call.swift @@ -77,13 +77,4 @@ extension Module { return .init(callee: callee, output: output, arguments: arguments, site: site) } - /// Returns `true` iff `o` is an `access [set]` instruction. - fileprivate func isBorrowSet(_ o: Operand) -> Bool { - guard - let i = o.instruction, - let s = self[i] as? Access - else { return false } - return s.capabilities == [.set] - } - } From 5339cd00edb73092234c27c86e705cd84cc68e07 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 15:17:00 +0200 Subject: [PATCH 03/13] Simplify 'Module.makeProjectBundle' --- Sources/FrontEnd/TypedProgram.swift | 8 +++++ Sources/IR/Emitter.swift | 23 +++---------- .../Operands/Instruction/ProjectBundle.swift | 32 +++++++++---------- 3 files changed, 29 insertions(+), 34 deletions(-) diff --git a/Sources/FrontEnd/TypedProgram.swift b/Sources/FrontEnd/TypedProgram.swift index 5e4c2fc15..adb362dff 100644 --- a/Sources/FrontEnd/TypedProgram.swift +++ b/Sources/FrontEnd/TypedProgram.swift @@ -428,6 +428,14 @@ public struct TypedProgram { } } + /// Returns the type of `d` specialized by `specialization` in `scopeOfUse`. + public func canonicalType( + of d: T.ID, specializedBy specialization: GenericArguments, in scopeOfUse: AnyScopeID + ) -> AnyType { + let t = specialize(self[d].type, for: specialization, in: scopeOfUse) + return canonical(t, in: scopeOfUse) + } + /// Returns the declarations of `d`' captures. /// /// If `d` is a member, its receiver is its only capture. Otherwise, this method returns diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 8805a3b33..a227e1996 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -2034,17 +2034,9 @@ struct Emitter { let (callee, captures) = emit(subscriptCallee: ast[e].callee) let arguments = captures + explicitArguments - var variants: [AccessEffect: Function.ID] = [:] - for v in ast[callee.bundle].impls { - variants[ast[v].introducer.value] = module.demandDeclaration(lowering: v) - } - - // Projection is evaluated last. - let t = canonicalType(of: callee.bundle, specializedBy: callee.arguments) - return insert( - module.makeProjectBundle( - applying: variants, of: callee, typed: SubscriptType(t)!, - to: arguments, at: ast[e].site))! + let s = module.makeProjectBundle( + applying: callee, to: arguments, in: insertionScope!, at: ast[e].site) + return insert(s)! } /// Inserts the IR for lvalue `e`. @@ -2148,14 +2140,9 @@ struct Emitter { let t = SubscriptType(canonicalType(of: d, specializedBy: specialization))! let r = insert(module.makeAccess(t.capabilities, from: receiver, at: site))! - var variants: [AccessEffect: Function.ID] = [:] - for v in ast[d].impls { - variants[ast[v].introducer.value] = module.demandDeclaration(lowering: v) - } - let s = module.makeProjectBundle( - applying: variants, of: .init(to: d, parameterizedBy: specialization), typed: t, - to: [r], at: site) + applying: .init(to: d, parameterizedBy: specialization), to: [r], + in: insertionScope!, at: site) return insert(s)! } diff --git a/Sources/IR/Operands/Instruction/ProjectBundle.swift b/Sources/IR/Operands/Instruction/ProjectBundle.swift index ebe53262b..d53c8fcf5 100644 --- a/Sources/IR/Operands/Instruction/ProjectBundle.swift +++ b/Sources/IR/Operands/Instruction/ProjectBundle.swift @@ -14,8 +14,8 @@ public struct ProjectBundle: Instruction { /// The arguments of the call. /// - /// Operands to non-`sink` inputs must be the result of an `access` instruction requesting a - /// capability for each variant in `callee` and having no use before `project`. + /// Operands to must be the result of an `access` instruction requesting a capability for each + /// variant in `callee` and having no use before `project_bundle`. public private(set) var operands: [Operand] /// The site of the code corresponding to that instruction. @@ -77,24 +77,24 @@ extension ProjectBundle: CustomStringConvertible { extension Module { /// Creates a `project_bundle` anchored at `site` that projects a value by applying one of the - /// given `variants` on `arguments`. The variants are defined in `bundle`, which is has type - /// `bundleType`. - /// - /// - Requires: `bundleType` is canonical and `variants` is not empty. - func makeProjectBundle( - applying variants: [AccessEffect: Function.ID], - of bundle: BundleReference, - typed bundleType: SubscriptType, + /// variants in `bundle` on `arguments`, canonicalizing types in `scopeOfUse`. + mutating func makeProjectBundle( + applying bundle: BundleReference, to arguments: [Operand], + in scopeOfUse: AnyScopeID, at site: SourceRange ) -> ProjectBundle { - precondition(bundleType[.isCanonical]) + var variants: [AccessEffect: Function.ID] = [:] + for v in program[bundle.bundle].impls { + variants[program[v].introducer.value] = demandDeclaration(lowering: v) + } + + let bundleType = program.canonicalType( + of: bundle.bundle, specializedBy: bundle.arguments, in: scopeOfUse) + let t = SubscriptType(bundleType)!.pure + return .init( - bundle: bundle, - pureCalleeType: bundleType.pure, - variants: variants, - operands: arguments, - site: site) + bundle: bundle, pureCalleeType: t, variants: variants, operands: arguments, site: site) } } From 2ec11b360f9b7ab4f72f1861f11e17295edea0d2 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 15:28:54 +0200 Subject: [PATCH 04/13] Refactor the representation of 'project_bundle' --- .../Analysis/Module+AccessReification.swift | 2 +- .../Operands/Instruction/ProjectBundle.swift | 30 +++++++++---------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Sources/IR/Analysis/Module+AccessReification.swift b/Sources/IR/Analysis/Module+AccessReification.swift index 2408cb41b..665468380 100644 --- a/Sources/IR/Analysis/Module+AccessReification.swift +++ b/Sources/IR/Analysis/Module+AccessReification.swift @@ -145,7 +145,7 @@ extension Module { arguments[a] = .register(insert(b, before: i)) } - let o = RemoteType(k, s.projection.bareType) + let o = RemoteType(k, s.projection) let reified = makeProject( o, applying: s.variants[k]!, specializedBy: s.bundle.arguments, to: arguments, at: s.site) replace(i, with: reified) diff --git a/Sources/IR/Operands/Instruction/ProjectBundle.swift b/Sources/IR/Operands/Instruction/ProjectBundle.swift index d53c8fcf5..e7b540b28 100644 --- a/Sources/IR/Operands/Instruction/ProjectBundle.swift +++ b/Sources/IR/Operands/Instruction/ProjectBundle.swift @@ -6,8 +6,11 @@ public struct ProjectBundle: Instruction { /// The subscript bundle implementing the projections. public let bundle: BundleReference - /// The pure functional type of the callee. - public let pureCalleeType: LambdaType + /// The parameters of the subscript. + public let parameters: [ParameterType] + + /// The type of the projected value. + public let projection: AnyType /// The subscripts implementing the projection. public let variants: [AccessEffect: Function.ID] @@ -24,14 +27,16 @@ public struct ProjectBundle: Instruction { /// Creates an instance with the given properties. fileprivate init( bundle: BundleReference, - pureCalleeType: LambdaType, variants: [AccessEffect: Function.ID], + parameters: [ParameterType], + projection: AnyType, operands: [Operand], site: SourceRange ) { self.bundle = bundle - self.pureCalleeType = pureCalleeType self.variants = variants + self.parameters = parameters + self.projection = projection self.operands = operands self.site = site } @@ -41,19 +46,9 @@ public struct ProjectBundle: Instruction { .init(variants.keys) } - /// The type of the projected value. - public var projection: RemoteType { - RemoteType(pureCalleeType.output)! - } - - /// The parameters of the projection. - public var parameters: LazyMapSequence<[CallableTypeParameter], ParameterType> { - pureCalleeType.inputs.lazy.map({ ParameterType($0.type)! }) - } - /// The types of the instruction's results. public var result: IR.`Type`? { - .address(projection.bareType) + .address(projection) } public mutating func replaceOperand(at i: Int, with new: Operand) { @@ -94,7 +89,10 @@ extension Module { let t = SubscriptType(bundleType)!.pure return .init( - bundle: bundle, pureCalleeType: t, variants: variants, operands: arguments, site: site) + bundle: bundle, variants: variants, + parameters: t.inputs.lazy.map({ ParameterType($0.type)! }), + projection: RemoteType(t.output)!.bareType, + operands: arguments, site: site) } } From d45061516a3527d3b05d22d0259e4059ab614b81 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 15:39:46 +0200 Subject: [PATCH 05/13] Remove needless label --- Sources/IR/FunctionID.swift | 2 +- Sources/IR/Module.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/IR/FunctionID.swift b/Sources/IR/FunctionID.swift index 41e121451..492d7cc4c 100644 --- a/Sources/IR/FunctionID.swift +++ b/Sources/IR/FunctionID.swift @@ -34,7 +34,7 @@ extension Function { } /// Creates the identity of the lowered form of `f` used as an initializer. - public init(initializer f: InitializerDecl.ID) { + public init(_ f: InitializerDecl.ID) { self.value = .lowered(AnyDeclID(f)) } diff --git a/Sources/IR/Module.swift b/Sources/IR/Module.swift index 1178a517b..09323e371 100644 --- a/Sources/IR/Module.swift +++ b/Sources/IR/Module.swift @@ -307,7 +307,7 @@ public struct Module { mutating func demandDeclaration(lowering d: InitializerDecl.ID) -> Function.ID { precondition(!program.ast[d].isMemberwise) - let f = Function.ID(initializer: d) + let f = Function.ID(d) if functions[f] != nil { return f } let parameters = program.accumulatedGenericParameters(in: d) From e452ab388785fe6cceacd8173fe760c56327cf74 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 15:46:49 +0200 Subject: [PATCH 06/13] Refactor the initializers of 'Function.ID' --- Sources/IR/FunctionID.swift | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Sources/IR/FunctionID.swift b/Sources/IR/FunctionID.swift index 492d7cc4c..626819a15 100644 --- a/Sources/IR/FunctionID.swift +++ b/Sources/IR/FunctionID.swift @@ -1,4 +1,5 @@ import Core +import Utils extension Function { @@ -28,19 +29,17 @@ extension Function { /// The value of this identity. public let value: Value - /// Creates the identity of the lowered form of `f`. - public init(_ f: FunctionDecl.ID) { - self.value = .lowered(AnyDeclID(f)) - } - - /// Creates the identity of the lowered form of `f` used as an initializer. - public init(_ f: InitializerDecl.ID) { - self.value = .lowered(AnyDeclID(f)) - } - - /// Creates the identity of the lowered form of `s`. - public init(_ s: SubscriptImpl.ID) { - self.value = .loweredSubscript(s) + /// Creates the identity of the lowered form of `d`, which is the declaration of a function, + /// initializer or subscript implementation.s + public init(_ d: T) { + switch d.kind { + case FunctionDecl.self, InitializerDecl.self: + self.value = .lowered(AnyDeclID(d)) + case SubscriptImpl.self: + self.value = .loweredSubscript(SubscriptImpl.ID(d)!) + default: + unreachable() + } } /// Creates the identity of the lowered form of `s`. From abf07eef38dc12da87520964be85c1bc54addaf1 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 15:47:53 +0200 Subject: [PATCH 07/13] Improve encapsulation --- Sources/IR/FunctionID.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/IR/FunctionID.swift b/Sources/IR/FunctionID.swift index 626819a15..eebd5d193 100644 --- a/Sources/IR/FunctionID.swift +++ b/Sources/IR/FunctionID.swift @@ -30,8 +30,8 @@ extension Function { public let value: Value /// Creates the identity of the lowered form of `d`, which is the declaration of a function, - /// initializer or subscript implementation.s - public init(_ d: T) { + /// initializer, or subscript implementation.s + init(_ d: T) { switch d.kind { case FunctionDecl.self, InitializerDecl.self: self.value = .lowered(AnyDeclID(d)) @@ -43,7 +43,7 @@ extension Function { } /// Creates the identity of the lowered form of `s`. - public init(_ s: SynthesizedFunctionDecl) { + init(_ s: SynthesizedFunctionDecl) { self.value = .synthesized(s) } From b17336dbc4717429153caf9aa38266e974ba5381 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 10:52:36 +0200 Subject: [PATCH 08/13] Move 'BundleReference.swift' to 'Sources/IR/' --- Sources/IR/{Operands/Constant => }/BundleReference.swift | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Sources/IR/{Operands/Constant => }/BundleReference.swift (100%) diff --git a/Sources/IR/Operands/Constant/BundleReference.swift b/Sources/IR/BundleReference.swift similarity index 100% rename from Sources/IR/Operands/Constant/BundleReference.swift rename to Sources/IR/BundleReference.swift From 42bf0dccea1b9939524e81fb9b82afc5b32ed185 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 13:03:14 +0200 Subject: [PATCH 09/13] Add an abstraction representing callees in IR --- Sources/IR/Callee.swift | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Sources/IR/Callee.swift diff --git a/Sources/IR/Callee.swift b/Sources/IR/Callee.swift new file mode 100644 index 000000000..39b2851f4 --- /dev/null +++ b/Sources/IR/Callee.swift @@ -0,0 +1,30 @@ +import Core + +/// The callee of a function call in the IR. +enum Callee { + + /// A direct reference to a function, initializer, or method implementation. + case direct(FunctionReference) + + /// A reference to a variant in a method bundle. + case bundle(BundleReference) + + /// A lambda. + case lambda(Operand) + + /// Creates an instance representing a reference to the lowered form of `d` in `module`, + /// specialized by`a` in `scopeOfUse`. + init( + _ d: AnyDeclID, specializedBy a: GenericArguments, + in module: inout Module, usedIn scopeOfUse: AnyScopeID + ) { + if let m = MethodDecl.ID(d) { + self = .bundle(BundleReference(to: m, specializedBy: a)) + } else { + let r = FunctionReference( + to: FunctionDecl.ID(d)!, in: &module, specializedBy: a, in: scopeOfUse) + self = .direct(r) + } + } + +} From 067b85eef4575cf79b5171dcad63dbcecf8cf1f8 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 13:05:47 +0200 Subject: [PATCH 10/13] Improve API and docs --- Sources/IR/BundleReference.swift | 4 ++-- Sources/IR/Emitter.swift | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Sources/IR/BundleReference.swift b/Sources/IR/BundleReference.swift index 18fd31cbb..8a2cdd2d5 100644 --- a/Sources/IR/BundleReference.swift +++ b/Sources/IR/BundleReference.swift @@ -1,6 +1,6 @@ import Core -/// A Hylo IR reference to a method or subscript bundle. +/// A reference to a method or subscript bundle. public struct BundleReference: Hashable { /// The ID of the referred bundle. @@ -10,7 +10,7 @@ public struct BundleReference: Hashable { public let arguments: GenericArguments /// Creates a reference to `s` parameterized by `a`. - public init(to s: T.ID, parameterizedBy a: GenericArguments) { + public init(to s: T.ID, specializedBy a: GenericArguments) { self.bundle = s self.arguments = a } diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index a227e1996..b41647ef1 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -1681,12 +1681,12 @@ struct Emitter { UNIMPLEMENTED() } - let b = BundleReference(to: SubscriptDecl.ID(d)!, parameterizedBy: a) + let b = BundleReference(to: SubscriptDecl.ID(d)!, specializedBy: a) return (b, []) case .member(let d, let a, let s) where d.kind == SubscriptDecl.self: // Callee is a member reference to a subscript declaration. - let b = BundleReference(to: SubscriptDecl.ID(d)!, parameterizedBy: a) + let b = BundleReference(to: SubscriptDecl.ID(d)!, specializedBy: a) // The callee's receiver is the sole capture. let receiver = emitLValue(receiver: s, at: ast[callee].site) @@ -2137,6 +2137,7 @@ struct Emitter { boundTo: receiver, declaredBy: i, specializedBy: specialization, at: site) } + let callee = BundleReference(to: d, specializedBy: specialization) let t = SubscriptType(canonicalType(of: d, specializedBy: specialization))! let r = insert(module.makeAccess(t.capabilities, from: receiver, at: site))! From fdec48d633fa34bbc3e65b17d6306a289297e9f2 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 13:06:46 +0200 Subject: [PATCH 11/13] Improve API and docs --- Sources/IR/Emitter.swift | 5 ++--- Sources/IR/Operands/Instruction/ProjectBundle.swift | 11 +++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index b41647ef1..c9f4f4f7c 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -2035,7 +2035,7 @@ struct Emitter { let arguments = captures + explicitArguments let s = module.makeProjectBundle( - applying: callee, to: arguments, in: insertionScope!, at: ast[e].site) + applying: .init(callee, in: insertionScope!), to: arguments, at: ast[e].site) return insert(s)! } @@ -2142,8 +2142,7 @@ struct Emitter { let r = insert(module.makeAccess(t.capabilities, from: receiver, at: site))! let s = module.makeProjectBundle( - applying: .init(to: d, parameterizedBy: specialization), to: [r], - in: insertionScope!, at: site) + applying: .init(callee, in: insertionScope!), to: [r], at: site) return insert(s)! } diff --git a/Sources/IR/Operands/Instruction/ProjectBundle.swift b/Sources/IR/Operands/Instruction/ProjectBundle.swift index e7b540b28..3cff64b72 100644 --- a/Sources/IR/Operands/Instruction/ProjectBundle.swift +++ b/Sources/IR/Operands/Instruction/ProjectBundle.swift @@ -72,24 +72,23 @@ extension ProjectBundle: CustomStringConvertible { extension Module { /// Creates a `project_bundle` anchored at `site` that projects a value by applying one of the - /// variants in `bundle` on `arguments`, canonicalizing types in `scopeOfUse`. + /// variants in `bundle` on `arguments`. mutating func makeProjectBundle( - applying bundle: BundleReference, + applying bundle: ScopedValue>, to arguments: [Operand], - in scopeOfUse: AnyScopeID, at site: SourceRange ) -> ProjectBundle { var variants: [AccessEffect: Function.ID] = [:] - for v in program[bundle.bundle].impls { + for v in program[bundle.value.bundle].impls { variants[program[v].introducer.value] = demandDeclaration(lowering: v) } let bundleType = program.canonicalType( - of: bundle.bundle, specializedBy: bundle.arguments, in: scopeOfUse) + of: bundle.value.bundle, specializedBy: bundle.value.arguments, in: bundle.scope) let t = SubscriptType(bundleType)!.pure return .init( - bundle: bundle, variants: variants, + bundle: bundle.value, variants: variants, parameters: t.inputs.lazy.map({ ParameterType($0.type)! }), projection: RemoteType(t.output)!.bareType, operands: arguments, site: site) From 590d31b2a088f64c6fe553493310ac2b16f1224a Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 13:07:08 +0200 Subject: [PATCH 12/13] Fix comments --- Sources/FrontEnd/TypedProgram.swift | 4 ++-- Sources/IR/Operands/Instruction/Call.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/FrontEnd/TypedProgram.swift b/Sources/FrontEnd/TypedProgram.swift index adb362dff..3a10e8d46 100644 --- a/Sources/FrontEnd/TypedProgram.swift +++ b/Sources/FrontEnd/TypedProgram.swift @@ -436,7 +436,7 @@ public struct TypedProgram { return canonical(t, in: scopeOfUse) } - /// Returns the declarations of `d`' captures. + /// Returns the declarations of `d`'s captures. /// /// If `d` is a member, its receiver is its only capture. Otherwise, this method returns /// `nonMemberCaptures(d)`. @@ -447,7 +447,7 @@ public struct TypedProgram { return nonMemberCaptures(of: d) } - /// Returns the declarations of `d`' captures. + /// Returns the declarations of `d`'s captures. /// /// If `d` is a member, its receiver is its only capture. Otherwise, this method returns /// `nonMemberCaptures(d)`. diff --git a/Sources/IR/Operands/Instruction/Call.swift b/Sources/IR/Operands/Instruction/Call.swift index f998aa06b..92c7f3f57 100644 --- a/Sources/IR/Operands/Instruction/Call.swift +++ b/Sources/IR/Operands/Instruction/Call.swift @@ -7,7 +7,7 @@ import Utils /// of the callee. `operands` must contain as many operands as the callee's type. public struct Call: Instruction { - /// The callee and arguments of the call. + /// The callee, the return storage, and arguments of the call. public private(set) var operands: [Operand] /// The site of the code corresponding to that instruction. From b7e2c36dbe01d584f9843033d0f9070b033a22dd Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 13:07:47 +0200 Subject: [PATCH 13/13] Generate IR for calls to method bundles --- Sources/IR/Emitter.swift | 79 +++++++++++----- Sources/IR/FunctionID.swift | 4 +- Sources/IR/Module.swift | 32 +++++++ .../IR/Operands/Instruction/CallBundle.swift | 91 +++++++++++++++++++ 4 files changed, 182 insertions(+), 24 deletions(-) create mode 100644 Sources/IR/Operands/Instruction/CallBundle.swift diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index c9f4f4f7c..6bfca5f9a 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -1164,8 +1164,14 @@ struct Emitter { let arguments = captures + explicitArguments // Call is evaluated last. - let o = insert(module.makeAccess(.set, from: storage, at: ast[e].site))! - insert(module.makeCall(applying: callee, to: arguments, writingResultTo: o, at: ast[e].site)) + switch callee { + case .direct(let r): + emitApply(.constant(r), to: arguments, writingResultTo: storage, at: ast[e].site) + case .lambda(let r): + emitApply(r, to: arguments, writingResultTo: storage, at: ast[e].site) + case .bundle(let r): + emitApply(r, to: arguments, writingResultTo: storage, at: ast[e].site) + } } /// Inserts the IR for storing the value of `e` to `storage`. @@ -1404,6 +1410,27 @@ struct Emitter { frames.top.setMayHoldCaptures(s) } + /// Inserts the IR for calling `callee` on `arguments`, storing the result to `storage`. + private mutating func emitApply( + _ callee: Operand, to arguments: [Operand], + writingResultTo storage: Operand, at site: SourceRange + ) { + let o = insert(module.makeAccess(.set, from: storage, at: site))! + insert(module.makeCall(applying: callee, to: arguments, writingResultTo: o, at: site)) + insert(module.makeEndAccess(o, at: site)) + } + + /// Inserts the IR for calling `callee` on `arguments`, storing the result to `storage`. + private mutating func emitApply( + _ callee: BundleReference, to arguments: [Operand], + writingResultTo storage: Operand, at site: SourceRange + ) { + let o = insert(module.makeAccess(.set, from: storage, at: site))! + insert(module.makeCallBundle( + applying: .init(callee, in: insertionScope!), to: arguments, writingResultTo: o, at: site)) + insert(module.makeEndAccess(o, at: site)) + } + /// Inserts the IR for given constructor `call`, which initializes storage `r` by applying /// initializer `d` parameterized by `a`. /// @@ -1579,7 +1606,7 @@ struct Emitter { /// - Requires: `callee` has a lambda type. private mutating func emit( functionCallee callee: AnyExprID - ) -> (callee: Operand, captures: [Operand]) { + ) -> (callee: Callee, captures: [Operand]) { switch callee.kind { case NameExpr.self: return emit(namedFunctionCallee: .init(callee)!) @@ -1589,41 +1616,36 @@ struct Emitter { return emit(functionCallee: ast[InoutExpr.ID(callee)!].subject) default: - return (emit(lambdaCallee: callee), []) + let f = emit(lambdaCallee: callee) + return (.lambda(f), []) } } /// Inserts the IR for given `callee` and returns `(c, a)`, where `c` is the callee's value and /// `a` are arguments to lifted parameters. - /// - /// - Requires: `callee` has a lambda type. private mutating func emit( namedFunctionCallee callee: NameExpr.ID - ) -> (callee: Operand, captures: [Operand]) { - let calleeType = LambdaType(canonical(program[callee].type))! - + ) -> (callee: Callee, captures: [Operand]) { switch program[callee].referredDecl { case .direct(let d, let a) where d.kind == FunctionDecl.self: // Callee is a direct reference to a function declaration. - guard calleeType.environment == .void else { + guard LambdaType(canonical(program[callee].type))!.environment == .void else { UNIMPLEMENTED() } let specialization = module.specialization(in: insertionFunction!).merging(a) - let r = FunctionReference( + let f = FunctionReference( to: FunctionDecl.ID(d)!, in: &module, specializedBy: specialization, in: insertionScope!) - return (.constant(r), []) + return (.direct(f), []) - case .member(let d, let a, let s) where d.kind == FunctionDecl.self: - // Callee is a member reference to a function or method. - let r = FunctionReference( - to: FunctionDecl.ID(d)!, in: &module, specializedBy: a, in: insertionScope!) - - // The callee's receiver is the sole capture. + case .member(let d, let a, let s): + // Callee is a member reference to a function or method. Its receiver is the only capture. + let specialization = module.specialization(in: insertionFunction!).merging(a) let receiver = emitLValue(receiver: s, at: ast[callee].site) - let k = RemoteType(calleeType.captures[0].type)?.access ?? .sink - let i = insert(module.makeAccess(k, from: receiver, at: ast[callee].site))! - return (Operand.constant(r), [i]) + let k = receiverCapabilities(program[callee].type) + let c = insert(module.makeAccess(k, from: receiver, at: ast[callee].site))! + let f = Callee(d, specializedBy: specialization, in: &module, usedIn: insertionScope!) + return (f, [c]) case .builtinFunction, .builtinType: // Calls to built-ins should have been handled already. @@ -1632,7 +1654,7 @@ struct Emitter { default: // Callee is a lambda. let f = emit(lambdaCallee: .init(callee)) - return (f, []) + return (.lambda(f), []) } } @@ -2394,6 +2416,19 @@ struct Emitter { return canonical(t) } + /// Returns the capabilities required by the receiver of `callee`, which is the type of a member + /// function or method bundle. + private func receiverCapabilities(_ callee: AnyType) -> AccessEffectSet { + switch canonical(callee).base { + case let t as LambdaType: + return [RemoteType(t.captures[0].type)?.access ?? .sink] + case let t as MethodType: + return t.capabilities + default: + unreachable() + } + } + /// Inserts a stack allocation for an object of type `t`. private mutating func emitAllocStack( for t: AnyType, at site: SourceRange diff --git a/Sources/IR/FunctionID.swift b/Sources/IR/FunctionID.swift index eebd5d193..22977970c 100644 --- a/Sources/IR/FunctionID.swift +++ b/Sources/IR/FunctionID.swift @@ -30,10 +30,10 @@ extension Function { public let value: Value /// Creates the identity of the lowered form of `d`, which is the declaration of a function, - /// initializer, or subscript implementation.s + /// initializer, method implementation, or subscript implementation.s init(_ d: T) { switch d.kind { - case FunctionDecl.self, InitializerDecl.self: + case FunctionDecl.self, InitializerDecl.self, MethodImpl.self: self.value = .lowered(AnyDeclID(d)) case SubscriptImpl.self: self.value = .loweredSubscript(SubscriptImpl.ID(d)!) diff --git a/Sources/IR/Module.swift b/Sources/IR/Module.swift index 09323e371..2865a9953 100644 --- a/Sources/IR/Module.swift +++ b/Sources/IR/Module.swift @@ -280,6 +280,30 @@ public struct Module { return f } + /// Returns the identity of the IR function corresponding to `d`. + mutating func demandDeclaration(lowering d: MethodImpl.ID) -> Function.ID { + let f = Function.ID(d) + if functions[f] != nil { return f } + + let parameters = program.accumulatedGenericParameters(in: d) + let output = program.canonical( + (program[d].type.base as! CallableType).output, in: program[d].scope) + + let inputs = loweredParameters(of: d) + + let entity = Function( + isSubscript: false, + site: program.ast[d].site, + linkage: program.isExported(d) ? .external : .module, + genericParameters: Array(parameters), + inputs: inputs, + output: output, + blocks: []) + addFunction(entity, for: f) + + return f + } + /// Returns the identity of the IR function corresponding to `d`. mutating func demandDeclaration(lowering d: SubscriptImpl.ID) -> Function.ID { let f = Function.ID(d) @@ -407,6 +431,14 @@ public struct Module { return result } + /// Returns the lowered declarations of `d`'s parameters. + private func loweredParameters(of d: MethodImpl.ID) -> [Parameter] { + let bundle = MethodDecl.ID(program[d].scope)! + let inputs = program.ast[bundle].parameters + let r = Parameter(AnyDeclID(program[d].receiver), capturedAs: program[d].receiver.type) + return [r] + inputs.map(pairedWithLoweredType(parameter:)) + } + /// Returns the lowered declarations of `d`'s parameters. /// /// `d`'s receiver comes first and is followed by `d`'s formal parameters, from left to right. diff --git a/Sources/IR/Operands/Instruction/CallBundle.swift b/Sources/IR/Operands/Instruction/CallBundle.swift new file mode 100644 index 000000000..8f642dbe6 --- /dev/null +++ b/Sources/IR/Operands/Instruction/CallBundle.swift @@ -0,0 +1,91 @@ +import Core +import Utils + +/// Invokes one variant of `bundle` with `arguments` and writes its result to `output`. +public struct CallBundle: Instruction { + + /// The method bundle implementing the variant to call. + public let bundle: BundleReference + + /// The type of the bundle. + public let bundleType: MethodType + + /// The variants of the bundle. + public let variants: [AccessEffect: Function.ID] + + /// The return storage and arguments of the call. + public private(set) var operands: [Operand] + + /// The site of the code corresponding to that instruction. + public let site: SourceRange + + /// Creates an instance with the given properties. + fileprivate init( + bundle: BundleReference, + bundleType: MethodType, + variants: [AccessEffect: Function.ID], + output: Operand, + arguments: [Operand], + site: SourceRange + ) { + self.bundle = bundle + self.bundleType = bundleType + self.variants = variants + self.operands = [output] + arguments + self.site = site + } + + /// The capabilities possibly requested on the receiver. + public var capabilities: AccessEffectSet { + .init(variants.keys) + } + + /// The location at which the result of `callee` is stored. + public var output: Operand { operands[0] } + + /// The arguments of the call. + public var arguments: ArraySlice { operands[1...] } + + public mutating func replaceOperand(at i: Int, with new: Operand) { + operands[i] = new + } + +} + +extension CallBundle: CustomStringConvertible { + + public var description: String { + "call_bundle \(capabilities) \(bundle)(\(list: arguments)) to \(output)" + } + +} + +extension Module { + + /// Creates a `call_bundle` anchored at `site` that calls one of the variants in `bundle` on + /// `arguments`. + mutating func makeCallBundle( + applying bundle: ScopedValue>, to arguments: [Operand], + writingResultTo output: Operand, + at site: SourceRange + ) -> CallBundle { + var variants: [AccessEffect: Function.ID] = [:] + for v in program[bundle.value.bundle].impls { + variants[program[v].introducer.value] = demandDeclaration(lowering: v) + } + + let bundleType = program.canonicalType( + of: bundle.value.bundle, specializedBy: bundle.value.arguments, in: bundle.scope) + let t = MethodType(bundleType)! + + precondition((t.inputs.count + 1) == arguments.count) + precondition(arguments.allSatisfy({ self[$0] is Access })) + precondition(isBorrowSet(output)) + + return .init( + bundle: bundle.value, bundleType: t, variants: variants, + output: output, arguments: arguments, + site: site) + } + +}