From 89fec6095971ad9ae8c0cfee0ff52f51edaf84fe Mon Sep 17 00:00:00 2001 From: Ajani Bilby <11359344+AjaniBilby@users.noreply.github.com> Date: Fri, 8 Mar 2024 17:15:27 +1100 Subject: [PATCH] linear types are now storable in variables --- source/compiler/codegen/context.ts | 154 ++++++++++-------- .../compiler/codegen/expression/container.ts | 74 +++++++++ source/compiler/codegen/expression/helper.ts | 24 +-- source/compiler/codegen/expression/operand.ts | 91 +++-------- source/compiler/codegen/expression/postfix.ts | 1 - source/compiler/codegen/expression/type.ts | 7 + source/compiler/codegen/scope.ts | 65 +++++--- source/compiler/codegen/variable.ts | 108 ++++-------- source/compiler/function.ts | 2 +- source/compiler/intrinsic.ts | 2 +- source/compiler/structure.ts | 4 +- 11 files changed, 273 insertions(+), 259 deletions(-) create mode 100644 source/compiler/codegen/expression/container.ts diff --git a/source/compiler/codegen/context.ts b/source/compiler/codegen/context.ts index cf39607..9326b35 100644 --- a/source/compiler/codegen/context.ts +++ b/source/compiler/codegen/context.ts @@ -6,13 +6,17 @@ import type { Scope } from "./scope.ts"; import * as banned from "~/compiler/codegen/banned.ts"; import Structure from "~/compiler/structure.ts"; -import { IntrinsicType, i16, i8, u16, u8 } from "~/compiler/intrinsic.ts"; +import { IntrinsicType, IntrinsicValue, i16, i8, u16, u8 } from "~/compiler/intrinsic.ts"; import { Instruction, AnyInstruction } from "~/wasm/index.ts"; import { AssertUnreachable, Panic } from "~/helper.ts"; import { OperandType } from "~/compiler/codegen/expression/type.ts"; import { CompileExpr } from "~/compiler/codegen/expression/index.ts"; import { none, never } from "~/compiler/intrinsic.ts"; import { Block } from "~/wasm/instruction/control-flow.ts"; +import { LinearType } from "~/compiler/codegen/expression/type.ts"; +import { IntrinsicVariable } from "~/compiler/codegen/variable.ts"; +import { StructVariable } from "~/compiler/codegen/variable.ts"; +import { Variable } from "~/compiler/codegen/variable.ts"; export class Context { file: File; @@ -75,82 +79,78 @@ function CompileDeclare(ctx: Context, syntax: Syntax.Term_Declare) { const type = syntax.value[1].value[0]; const expr = syntax.value[2].value[0]; - if (banned.namespaces.includes(name)) - Panic(`${colors.red("Error")}: You're not allowed to call a variable ${name}\n`, { - path: ctx.file.path, - name: ctx.file.name, - ref: syntax.value[0].value[0].ref - }) + if (banned.namespaces.includes(name)) Panic( + `${colors.red("Error")}: You're not allowed to call a variable ${name}\n`, + { path: ctx.file.path, name: ctx.file.name, ref: syntax.value[0].value[0].ref } + ) + + if (ctx.scope.hasVariable(name)) Panic(`${colors.red("Error")}: Variable ${name} is already declared\n`, + { path: ctx.file.path, name: ctx.file.name, ref: syntax.ref } + ); let typeRef: Namespace | null = null; if (type) { typeRef = ctx.file.get(type.value[0]); - if (typeRef === null || !(typeRef instanceof IntrinsicType) && !(typeRef instanceof Structure)) - Panic(`${colors.red("Error")}: Cannot find type\n`, { - path: ctx.file.path, - name: ctx.file.name, - ref: type.ref - }) - - if (typeRef === i8 || typeRef === u8 || typeRef === i16 || typeRef === u16) - Panic(`${colors.red("Error")}: Cannot explicitly use virtual integer types\n`, { - path: ctx.file.path, - name: ctx.file.name, - ref: type.ref - }) + if (typeRef === null || !(typeRef instanceof IntrinsicType) && !(typeRef instanceof Structure)) Panic( + `${colors.red("Error")}: Cannot find type\n`, + { path: ctx.file.path, name: ctx.file.name, ref: type.ref } + ) + + if (typeRef === i8 || typeRef === u8 || typeRef === i16 || typeRef === u16) Panic( + `${colors.red("Error")}: Cannot explicitly use virtual integer types\n`, + { path: ctx.file.path, name: ctx.file.name, ref: type.ref } + ) } if (!expr) { - if (!typeRef) - Panic(`${colors.red("Error")}: Declared variables must have an explicit or an inferred type\n`, { - path: ctx.file.path, - name: ctx.file.name, - ref: syntax.ref - }) - - const variable = ctx.scope.registerVariable(name, typeRef, syntax.ref); - if (!variable) - Panic(`${colors.red("Error")}: Variable ${name} is already declared\n`, { - path: ctx.file.path, - name: ctx.file.name, - ref: syntax.ref - }); + if (!typeRef) Panic( + `${colors.red("Error")}: Declared variables must have an explicit or an inferred type\n`, + { path: ctx.file.path, name: ctx.file.name, ref: syntax.ref } + ) + + if (typeRef instanceof Structure) { + typeRef.link(); + const alloc = ctx.scope.stack.allocate(typeRef.size, typeRef.align); + const linear = LinearType.make(typeRef, alloc, ctx.file.owner.project.stackBase); + + ctx.scope.registerVariable(name, linear, syntax.ref); + } else { + ctx.scope.registerVariable(name, typeRef.value, syntax.ref); + } return; } const value = expr.value[0]; const resolveType = CompileExpr(ctx, value, typeRef || undefined); - if (!typeRef && !resolveType) Panic( - `${colors.red("Error")}: Unable to determine type\n`, - { path: ctx.file.path, name: ctx.file.name, ref: syntax.ref } - ); - if (typeRef && resolveType !== typeRef) Panic( - `${colors.red("Error")}: type ${typeRef.name} != type ${resolveType.getTypeName()}\n`, - { path: ctx.file.path, name: ctx.file.name, ref: type?.ref || syntax.ref } - ) - if (!(resolveType instanceof IntrinsicType)) { - // TODO - // Panic( - // `${colors.red("Error")}: Cannot assign variable to non-intrinsic type\n`, - // { path: ctx.file.path, name: ctx.file.name, ref: type?.ref || syntax.ref } - // ) + // Check expected, and inferred matches + if (typeRef) { + let baseType = resolveType; - return; + if ( resolveType instanceof LinearType ) baseType = resolveType.type; + if ( resolveType instanceof IntrinsicValue ) baseType = resolveType.type; + + if (typeRef !== baseType) Panic( + `${colors.red("Error")}: type ${typeRef.name} != type ${resolveType.getTypeName()}\n`, + { path: ctx.file.path, name: ctx.file.name, ref: type?.ref || syntax.ref } + ) } - const variable = ctx.scope.registerVariable(name, typeRef || resolveType, syntax.ref); - if (!variable) - Panic(`${colors.red("Error")}: Variable ${name} is already declared\n`, { - path: ctx.file.path, - name: ctx.file.name, - ref: syntax.ref - }); + if (!(resolveType instanceof IntrinsicValue || resolveType instanceof LinearType)) Panic( + `${colors.red("Error")}: Cannot assign to non-runtime type ${resolveType.getTypeName()}\n`, + { path: ctx.file.path, name: ctx.file.name, ref: syntax.ref } + ) + + const variable = ctx.scope.registerVariable(name, resolveType, syntax.ref); variable.markDefined(); - ctx.block.push(Instruction.local.set(variable.register.ref)); + if (variable instanceof IntrinsicVariable) { + ctx.block.push(Instruction.local.set(variable.register.ref)); + } else if (variable instanceof StructVariable) { + // No-op for struct as value is already written to stack allocator addr + } else AssertUnreachable(variable); } function CompileAssign(ctx: Context, syntax: Syntax.Term_Assign) { @@ -158,25 +158,35 @@ function CompileAssign(ctx: Context, syntax: Syntax.Term_Assign) { const value = syntax.value[1]; const variable = ctx.scope.getVariable(name, false); - if (!variable) - Panic(`${colors.red("Error")}: Undeclared variable ${name}\n`, { - path: ctx.file.path, - name: ctx.file.name, - ref: syntax.ref - }); - - const resolveType = CompileExpr(ctx, value, variable.type); - if (resolveType !== variable.type) Panic( - `${colors.red("Error")}: type ${variable.name} != type ${resolveType.getTypeName()}\n`, + if (!variable) Panic( + `${colors.red("Error")}: Undeclared variable ${name}\n`, { path: ctx.file.path, name: ctx.file.name, ref: syntax.ref } ); - if (!(resolveType instanceof IntrinsicType)) Panic( - `${colors.red("Error")}: Cannot assign variable to non-intrinsic type\n`, - { path: ctx.file.path, name: ctx.file.name, ref: syntax.ref } - ) + const resolveType = CompileExpr(ctx, value, variable.getBaseType()); + + // Check expected, and inferred match + if (variable.type) { + let baseType = resolveType; + + if ( resolveType instanceof LinearType ) baseType = resolveType.type; + if ( resolveType instanceof IntrinsicValue ) baseType = resolveType.type; + + if (variable.type !== baseType) Panic( + `${colors.red("Error")}: type ${variable.type.getTypeName()} != type ${resolveType.getTypeName()}\n`, + { path: ctx.file.path, name: ctx.file.name, ref: syntax.ref } + ) + } + + if (variable instanceof IntrinsicVariable) { + ctx.block.push(Instruction.local.set(variable.register.ref)); + } else if (variable instanceof StructVariable) { + // TODO: drop previous value + // TODO: move operation + console.warn(`Warn: Unimplemented struct re-assign causing unsafe no-op`); + } else AssertUnreachable(variable); + - ctx.block.push(Instruction.local.set(variable.register.ref)); variable.markDefined(); } diff --git a/source/compiler/codegen/expression/container.ts b/source/compiler/codegen/expression/container.ts new file mode 100644 index 0000000..062c92a --- /dev/null +++ b/source/compiler/codegen/expression/container.ts @@ -0,0 +1,74 @@ +import * as colors from "https://deno.land/std@0.201.0/fmt/colors.ts"; + +import type * as Syntax from "~/bnf/syntax.d.ts"; +import Structure from "~/compiler/structure.ts"; +import { IsContainerType, LinearType, SolidType, OperandType } from "~/compiler/codegen/expression/type.ts"; +import { Panic, LatentOffset } from "~/helper.ts"; +import { IntrinsicValue } from "~/compiler/intrinsic.ts"; +import { CompileExpr } from "~/compiler/codegen/expression/index.ts"; +import { Instruction } from "~/wasm/index.ts"; +import { Context } from "~/compiler/codegen/context.ts"; +import { Store } from "~/compiler/codegen/expression/helper.ts"; +import { SourceView } from "~/parser.ts"; + +export function StructBuilder(ctx: Context, syntax: Syntax.Term_Container, expect?: SolidType): OperandType { + if (!(expect instanceof Structure)) Panic( + `${colors.red("Error")}: Unable to infer struct type\n`, { + path: ctx.file.path, name: ctx.file.name, ref: syntax.ref + }); + if (expect instanceof Structure) expect.link(); + + const alloc = ctx.scope.stack.allocate(expect.size, expect.align); + const linear = LinearType.make(expect, alloc, ctx.file.owner.project.stackBase); + + function* iterator() { + const base = syntax.value[0].value[0]; + if (!base) return; + + yield base.value[0]; // first + for (const next of base.value[1].value) yield next.value[0]; // comma chained + + return; + } + + for (const item of iterator()) { + const elm = item.value[0]; + if (elm.type === "container_value") { + console.error( + `${colors.red("Error")}: Unexpected array value as struct member\n` + + SourceView(ctx.file.path, ctx.file.name, elm.ref) + ); + ctx.file.markFailure(); + continue; + } + + const name = elm.value[0].value[0].value; + const attr = expect.get(name); + if (!attr) Panic( + `${colors.red("Error")}: Unknown attribute ${name} in struct ${expect.name}\n`, { + path: ctx.file.path, name: ctx.file.name, ref: elm.ref + }); + + ctx.block.push(Instruction.const.i32(0)); + const expr = CompileExpr(ctx, elm.value[1], attr.type); + if (expr instanceof IntrinsicValue) { + Store(ctx, expr.type, new LatentOffset(alloc.getOffset(), attr.offset)); + } else Panic( + `${colors.red("Error")}: Only intrinsics are currently supported\n`, { + path: ctx.file.path, name: ctx.file.name, ref: elm.ref + }); + } + + return linear; +} + +export function ArrayBuilder(ctx: Context, syntax: Syntax.Term_Container, expect?: SolidType): OperandType { + if (!expect) Panic( + `${colors.red("Error")}: Unsupported untyped container creation\n`, { + path: ctx.file.path, name: ctx.file.name, ref: syntax.ref + }); + + Panic(`${colors.red("Error")}: Arrays are currently unsupported\n`, + { path: ctx.file.path, name: ctx.file.name, ref: syntax.ref } + ); +} \ No newline at end of file diff --git a/source/compiler/codegen/expression/helper.ts b/source/compiler/codegen/expression/helper.ts index 6dc6111..b0eaf69 100644 --- a/source/compiler/codegen/expression/helper.ts +++ b/source/compiler/codegen/expression/helper.ts @@ -9,10 +9,10 @@ export function Store(ctx: Context, type: SolidType, offset: number | LatentOffs if (!(type instanceof IntrinsicType)) Panic("Unimplemented"); switch (type.name) { - case "u32": case "i32": ctx.block.push(Instruction.i32.store(offset, 1)); break; - case "u64": case "i64": ctx.block.push(Instruction.i64.store(offset, 1)); break; - case "u8": case "i8": ctx.block.push(Instruction.i32.store8(offset, 1)); break; - case "u16": case "i16": ctx.block.push(Instruction.i32.store16(offset, 1)); break; + case "u32": case "i32": ctx.block.push(Instruction.i32.store(offset, 0)); break; + case "u64": case "i64": ctx.block.push(Instruction.i64.store(offset, 0)); break; + case "u8": case "i8": ctx.block.push(Instruction.i32.store8(offset, 0)); break; + case "u16": case "i16": ctx.block.push(Instruction.i32.store16(offset, 0)); break; case "f32": ctx.block.push(Instruction.f32.store(offset, 1)); break; case "f64": ctx.block.push(Instruction.f64.store(offset, 1)); break; @@ -25,14 +25,14 @@ export function Load(ctx: Context, type: SolidType, offset: number | LatentOffse if (!(type instanceof IntrinsicType)) Panic("Unimplemented"); switch (type.name) { - case "u32": case "i32": ctx.block.push(Instruction.i32.load(offset, 1)); break; - case "u64": case "i64": ctx.block.push(Instruction.i64.load(offset, 1)); break; - case "u8": ctx.block.push(Instruction.i32.load8_u(offset, 1)); break; - case "i8": ctx.block.push(Instruction.i32.load8_s(offset, 1)); break; - case "u16": ctx.block.push(Instruction.i32.load16_u(offset, 1)); break; - case "i16": ctx.block.push(Instruction.i32.load16_s(offset, 1)); break; - case "f32": ctx.block.push(Instruction.f32.load(offset, 1)); break; - case "f64": ctx.block.push(Instruction.f64.load(offset, 1)); break; + case "u32": case "i32": ctx.block.push(Instruction.i32.load(offset, 0)); break; + case "u64": case "i64": ctx.block.push(Instruction.i64.load(offset, 0)); break; + case "u8": ctx.block.push(Instruction.i32.load8_u(offset, 0)); break; + case "i8": ctx.block.push(Instruction.i32.load8_s(offset, 0)); break; + case "u16": ctx.block.push(Instruction.i32.load16_u(offset, 0)); break; + case "i16": ctx.block.push(Instruction.i32.load16_s(offset, 0)); break; + case "f32": ctx.block.push(Instruction.f32.load(offset, 0)); break; + case "f64": ctx.block.push(Instruction.f64.load(offset, 0)); break; default: Panic(`Unhandled store type ${type.name}`); } diff --git a/source/compiler/codegen/expression/operand.ts b/source/compiler/codegen/expression/operand.ts index 0270958..277a7a9 100644 --- a/source/compiler/codegen/expression/operand.ts +++ b/source/compiler/codegen/expression/operand.ts @@ -1,18 +1,18 @@ import * as colors from "https://deno.land/std@0.201.0/fmt/colors.ts"; import type * as Syntax from "~/bnf/syntax.d.ts"; -import Structure from "~/compiler/structure.ts"; -import { IsContainerType, LinearType, SolidType, OperandType, IsSolidType } from "~/compiler/codegen/expression/type.ts"; -import { AssertUnreachable, Panic, LatentOffset } from "~/helper.ts"; -import { IntrinsicType, IntrinsicValue, VirtualType, bool } from "~/compiler/intrinsic.ts"; +import { LinearType, SolidType, OperandType } from "~/compiler/codegen/expression/type.ts"; +import { IntrinsicVariable, StructVariable } from "~/compiler/codegen/variable.ts"; +import { IntrinsicValue, VirtualType, bool } from "~/compiler/intrinsic.ts"; +import { ArrayBuilder, StructBuilder } from "~/compiler/codegen/expression/container.ts"; +import { AssertUnreachable, Panic } from "~/helper.ts"; import { CompilePostfixes } from "~/compiler/codegen/expression/postfix.ts"; import { CompileConstant } from "~/compiler/codegen/expression/constant.ts"; import { CompilePrefix } from "~/compiler/codegen/expression/prefix.ts"; import { CompileExpr } from "~/compiler/codegen/expression/index.ts"; -import { Instruction } from "~/wasm/index.ts"; import { IsNamespace } from "~/compiler/file.ts"; +import { Instruction } from "~/wasm/index.ts"; import { Context } from "~/compiler/codegen/context.ts"; -import { Store } from "~/compiler/codegen/expression/helper.ts"; export function CompileArg(ctx: Context, syntax: Syntax.Term_Expr_arg, expect?: SolidType): OperandType { @@ -37,63 +37,14 @@ export function CompileArg(ctx: Context, syntax: Syntax.Term_Expr_arg, expect?: } function CompileContainer(ctx: Context, syntax: Syntax.Term_Container, expect?: SolidType): OperandType { - if (!expect) Panic( - `${colors.red("Error")}: Unsupported untyped container creation\n`, { - path: ctx.file.path, name: ctx.file.name, ref: syntax.ref - }); - - if (!IsContainerType(expect)) Panic( - `${colors.red("Error")}: Expecting non-container type, unknown container resolution type\n`, { - path: ctx.file.path, name: ctx.file.name, ref: syntax.ref - }); - - if (expect instanceof Structure) expect.link(); - const alloc = ctx.scope.stack.allocate(expect.size, expect.align); - - function* iterator() { - const base = syntax.value[0].value[0]; - if (!base) return; - - // first - yield base.value[0]; - - // comma chained - for (const next of base.value[1].value) yield next.value[0]; - } - - for (const item of iterator()) { - const elm = item.value[0]; - if (elm.type === "container_value") Panic( - `${colors.red("Error")}: Arrays are currently unsupported container types\n`, { - path: ctx.file.path, name: ctx.file.name, ref: elm.ref - }); - - const name = elm.value[0].value[0].value; - if (!(expect instanceof Structure)) Panic( - `${colors.red("Error")}: Cannot assign .${name} to an array\n`, { - path: ctx.file.path, name: ctx.file.name, ref: elm.ref - }); - - const attr = expect.get(name); - if (!attr) Panic( - `${colors.red("Error")}: Unknown attribute ${name} in struct ${expect.name}\n`, { - path: ctx.file.path, name: ctx.file.name, ref: elm.ref - }); - - ctx.block.push(Instruction.const.i32(0)); - const expr = CompileExpr(ctx, elm.value[1], attr.type); - if (!IsSolidType(expr)) Panic( - `${colors.red("Error")}: Must be a solid type\n`, { - path: ctx.file.path, name: ctx.file.name, ref: elm.ref + switch (syntax.value[0].value[0]?.value[0].value[0].type) { + case "container_map": return StructBuilder(ctx, syntax, expect); + case "container_value": return ArrayBuilder(ctx, syntax, expect); + default: Panic( + `Unable to determine container type`, { + path: ctx.file.path, name: ctx.file.name, ref: syntax.ref }); - - Store(ctx, expr, new LatentOffset(alloc.getOffset(), attr.offset)); } - - // TODO: Proper consumption and freeing of allocation - alloc.free(); - - return expect; } function CompileBrackets(ctx: Context, syntax: Syntax.Term_Expr_brackets, expect?: SolidType) { @@ -112,14 +63,18 @@ function CompileName(ctx: Context, syntax: Syntax.Term_Name) { return found; } - if (!variable.isDefined) Panic(`${colors.red("Error")}: Variable ${name} has no value assigned to it\n`, { - path: ctx.file.path, name: ctx.file.name, ref: syntax.ref - }); + if (variable instanceof IntrinsicVariable) { + if (!variable.isDefined) Panic( + `${colors.red("Error")}: Variable ${name} has no value assigned to it\n`, + { path: ctx.file.path, name: ctx.file.name, ref: syntax.ref } + ); + - ctx.block.push(Instruction.local.get(variable.register.ref)); - return variable.type instanceof IntrinsicType - ? variable.type.value - : variable.type; + ctx.block.push(Instruction.local.get(variable.register.ref)); + return variable.type.value; + } else if (variable instanceof StructVariable) { + return variable.type; + } else AssertUnreachable(variable); } function CompileIf(ctx: Context, syntax: Syntax.Term_If, expect?: SolidType) { diff --git a/source/compiler/codegen/expression/postfix.ts b/source/compiler/codegen/expression/postfix.ts index 6ce68f1..d2cf618 100644 --- a/source/compiler/codegen/expression/postfix.ts +++ b/source/compiler/codegen/expression/postfix.ts @@ -7,7 +7,6 @@ import { OperandType } from "~/compiler/codegen/expression/type.ts"; import { CompileExpr } from "~/compiler/codegen/expression/index.ts"; import { IsNamespace } from "~/compiler/file.ts"; import { Instruction } from "~/wasm/index.ts"; -import { LinearType } from "~/compiler/codegen/expression/type.ts"; import { Context } from "~/compiler/codegen/context.ts"; diff --git a/source/compiler/codegen/expression/type.ts b/source/compiler/codegen/expression/type.ts index 94082e6..ea9dd6f 100644 --- a/source/compiler/codegen/expression/type.ts +++ b/source/compiler/codegen/expression/type.ts @@ -172,9 +172,16 @@ export class LinearType { } getTypeName() { + if (this.type instanceof Structure) return this.type.name; + return this.type.getTypeName(); } + getBaseType() { + if (this.type instanceof Structure) return this.type; + return this.type.type; + } + like(other: OperandType): boolean { if (other instanceof LinearType) return this.type === other.type; if (other instanceof IntrinsicValue) return this.type === other; diff --git a/source/compiler/codegen/scope.ts b/source/compiler/codegen/scope.ts index 10a06a9..dcca531 100644 --- a/source/compiler/codegen/scope.ts +++ b/source/compiler/codegen/scope.ts @@ -1,11 +1,13 @@ -import { RegisterAllocator } from "./allocation/registers.ts"; +import * as colors from "https://deno.land/std@0.201.0/fmt/colors.ts"; + +import { IntrinsicVariable, StructVariable, Variable } from "~/compiler/codegen/variable.ts"; +import { AssertUnreachable, Panic } from "~/helper.ts"; +import { RegisterAllocator } from "~/compiler/codegen/allocation/registers.ts"; +import { StackAllocator } from "~/compiler/codegen/allocation/stack.ts"; +import { IntrinsicValue } from "~/compiler/intrinsic.ts"; import { ReferenceRange } from "~/parser.ts"; -import { MakeVariable } from "~/compiler/codegen/variable.ts"; -import { SolidType } from "~/compiler/codegen/expression/type.ts"; -import { TypeSystem } from "~/compiler/codegen/variable.ts" +import { LinearType } from "~/compiler/codegen/expression/type.ts"; import { Function } from "~/wasm/function.ts"; -import { Variable } from "~/compiler/codegen/variable.ts"; -import { StackAllocator } from "~/compiler/codegen/allocation/stack.ts"; export class Scope { _parent: Scope | null; @@ -30,28 +32,39 @@ export class Scope { this.vars = {}; } - registerArgument(name: string, type: SolidType, ref: ReferenceRange) { - this.vars[name] = MakeVariable( - name, type, - this.register, - true, - ref - ); + registerArgument(name: string, type: IntrinsicValue | LinearType, ref: ReferenceRange) { + if (this.vars[name]) throw new Error(`Attempting to rebind variable ${name}`); + + if (type instanceof IntrinsicValue) { + this.vars[name] = new IntrinsicVariable( + name, type.type, + this.register.allocate(type.type.bitcode, true), + ref + ); + this.vars[name].markDefined(); + } else if (type instanceof LinearType) { + Panic( `${colors.red("Error")}: Structs as arguments are currently unsupported\n` ); + // this.vars[name] = new StructVariable(name, type); + // this.vars[name].markDefined(); + } else AssertUnreachable(type); this._localRegs = this.register._regs.length; return this.vars[name]; } - registerVariable(name: string, type: SolidType, ref: ReferenceRange) { - if (this.vars[name]) return null; + registerVariable(name: string, type: IntrinsicValue | LinearType, ref: ReferenceRange) { + if (this.vars[name]) throw new Error(`Attempting to rebind variable ${name}`); - this.vars[name] = MakeVariable( - name, type, - this.register, - false, - ref - ); + if (type instanceof IntrinsicValue) { + this.vars[name] = new IntrinsicVariable( + name, type.type, + this.register.allocate(type.type.bitcode, false), + ref + ); + } else if (type instanceof LinearType) { + this.vars[name] = new StructVariable(name, type); + } else AssertUnreachable(type); return this.vars[name]; } @@ -66,7 +79,7 @@ export class Scope { if (readOnly) return inherited; // Don't both cloning if the value can't be consumed in this scope - if (inherited.storage === TypeSystem.Normal) return inherited; + if (inherited instanceof LinearType) return inherited; this.vars[name] = inherited.clone(); } @@ -74,15 +87,17 @@ export class Scope { return null; } + hasVariable(name: string) { + return !!this.vars[name]; + } + child() { return new Scope(this); } - cleanup() { for (const name in this.vars) { - if (!this.vars[name].isLocal) continue; - this.vars[name].register.free(); + this.vars[name].cleanup(); } } } \ No newline at end of file diff --git a/source/compiler/codegen/variable.ts b/source/compiler/codegen/variable.ts index fafca48..8aa097d 100644 --- a/source/compiler/codegen/variable.ts +++ b/source/compiler/codegen/variable.ts @@ -1,29 +1,13 @@ -import Structure from "~/compiler/structure.ts"; -import { RegisterAllocator } from "./allocation/registers.ts"; -import { AssertUnreachable } from "~/helper.ts"; import { ReferenceRange } from "~/parser.ts"; -import { SolidType } from "~/compiler/codegen/expression/type.ts"; import { IntrinsicType } from "~/compiler/intrinsic.ts"; -import { Register } from "./allocation/registers.ts"; -import { u32 } from "~/compiler/intrinsic.ts"; - -export enum TypeSystem { - Affine, - Normal -} +import { LinearType } from "~/compiler/codegen/expression/type.ts"; +import { Register } from "~/compiler/codegen/allocation/registers.ts"; export type Variable = IntrinsicVariable | StructVariable; -export function MakeVariable(name: string, type: SolidType, register: RegisterAllocator, isArg: boolean, ref: ReferenceRange) { - if (type instanceof IntrinsicType) return new IntrinsicVariable(name, type, register, isArg, ref); - if (type instanceof Structure) return new StructVariable(name, type, register, isArg, ref); - AssertUnreachable(type); -} - export class IntrinsicVariable { name: string; type: IntrinsicType; - storage: TypeSystem; register: Register; lastDefined: ReferenceRange | null; @@ -31,27 +15,26 @@ export class IntrinsicVariable { isDefined: boolean; isGlobal: boolean; isClone: boolean; - isLocal: boolean; modifiedAt: ReferenceRange; - constructor(name: string, type: IntrinsicType, register: RegisterAllocator | Register, isArg: boolean, ref: ReferenceRange) { + constructor(name: string, type: IntrinsicType, register: Register, ref: ReferenceRange) { this.name = name; this.type = type; - this.storage = TypeSystem.Normal; this.modifiedAt = ref; - this.isDefined = isArg; + this.isDefined = false; this.isClone = false; this.isGlobal = false; - this.isLocal = !isArg; - this.register = register instanceof RegisterAllocator - ? register.allocate(type.bitcode, isArg) - : register; + this.register = register; this.lastDefined = ref; } + getBaseType() { + return this.type; + } + markDefined() { this.lastDefined = null; this.isDefined = true; @@ -63,22 +46,25 @@ export class IntrinsicVariable { markGlobal() { this.isGlobal = true; - this.isLocal = false; this.isClone = false; this.markDefined(); } clone() { - const clone = new IntrinsicVariable(this.name, this.type, this.register, !this.isLocal, this.modifiedAt); + const clone = new IntrinsicVariable(this.name, this.type, this.register, this.modifiedAt); clone.lastDefined = this.lastDefined; clone.isDefined = this.isDefined; clone.isGlobal = this.isGlobal; - clone.isLocal = false; clone.isClone = true; return clone; } + cleanup () { + if (this.isClone) return; + this.register.free(); + } + toBinary() { return this.register.type; } @@ -87,76 +73,44 @@ export class IntrinsicVariable { export class StructVariable { name: string; - type: Structure; - storage: TypeSystem; - register: Register; + type: LinearType; - lastDefined: ReferenceRange | null; - - isDefined: boolean; - isGlobal: boolean; isClone: boolean; - isLocal: boolean; - modifiedAt: ReferenceRange; - - mask: { [key: string]: Variable }; - constructor(name: string, type: Structure, register: RegisterAllocator | Register, isArg: boolean, ref: ReferenceRange) { + constructor(name: string, type: LinearType) { this.name = name; this.type = type; - this.storage = TypeSystem.Normal; - this.modifiedAt = ref; - this.isDefined = false; - this.isGlobal = false; - this.isLocal = !isArg; this.isClone = false; - this.mask = {}; - - this.register = register instanceof RegisterAllocator - ? register.allocate(u32.bitcode) - : register; - - // Ensure the type is actually linked - type.link(); + } - this.lastDefined = ref; + getBaseType() { + return this.type.getBaseType(); } markDefined() { - this.lastDefined = null; - this.isDefined = true; + this.type.markAssigned(); } markUndefined(ref: ReferenceRange) { - this.lastDefined = ref; - this.isDefined = false; - } - - markGlobal() { - this.isGlobal = true; - this.isLocal = false; - this.isClone = false; - this.markDefined(); + this.type.markConsumed(ref); } markArgument() { - this.isGlobal = false; - this.isLocal = false; - this.isClone = false; this.markDefined(); } clone() { - const clone = new StructVariable(this.name, this.type, this.register, !this.isLocal, this.modifiedAt); - clone.lastDefined = this.lastDefined; - clone.isDefined = this.isDefined; - clone.isGlobal = this.isGlobal; - clone.isLocal = false; - clone.isClone = true; + const clone = new StructVariable(this.name, this.type); + clone.isClone = true; return clone; } - toBinary() { - return this.register.type; + cleanup() { + if (this.isClone) return; + this.type.alloc.free(); } + + // toBinary() { + // return this.register.type; + // } } \ No newline at end of file diff --git a/source/compiler/function.ts b/source/compiler/function.ts index de79dcd..f59dda4 100644 --- a/source/compiler/function.ts +++ b/source/compiler/function.ts @@ -123,7 +123,7 @@ export default class Function { const scope = new Scope(func); for (const arg of this.arguments) { - scope.registerArgument(arg.name, arg.type, arg.ref) + scope.registerArgument(arg.name, arg.type.value, arg.ref) } const ctx = new Context(this.getFile(), scope, func.code); diff --git a/source/compiler/intrinsic.ts b/source/compiler/intrinsic.ts index e9984c1..9aa721d 100644 --- a/source/compiler/intrinsic.ts +++ b/source/compiler/intrinsic.ts @@ -25,7 +25,7 @@ export class IntrinsicType { } getTypeName() { - return this.name; + return "type " + this.name; } like (other: OperandType) { diff --git a/source/compiler/structure.ts b/source/compiler/structure.ts index 2d6706c..1c017aa 100644 --- a/source/compiler/structure.ts +++ b/source/compiler/structure.ts @@ -84,7 +84,7 @@ export default class Structure { const scope = this.owner; const type = scope.get(line.value[1]); - if (type === null) { + if (!type) { console.error( `${colors.red("Error")}: Unable to resolve type\n` + SourceView(this.owner.path, this.owner.name, line.value[1].ref) @@ -199,7 +199,7 @@ export default class Structure { } getTypeName() { - return this.name; + return "type " + this.name; } declarationView(): string {