This commit is contained in:
nora 2023-07-31 20:26:50 +02:00
parent b779a51ef5
commit f582a5b4c3
9 changed files with 248 additions and 85 deletions

View file

@ -81,6 +81,9 @@ export class ItemId {
} }
toString(): string { toString(): string {
if (this.crateId === 0) {
return `${this.itemIdx}`;
}
return `[${this.crateId}@${this.itemIdx}]`; return `[${this.crateId}@${this.itemIdx}]`;
} }
} }
@ -105,6 +108,10 @@ export type ItemKind<P extends Phase> =
| { | {
kind: "extern"; kind: "extern";
node: ExternItem; node: ExternItem;
}
| {
kind: "global";
node: GlobalItem<P>;
}; };
export type Item<P extends Phase> = ItemKind<P> & { export type Item<P extends Phase> = ItemKind<P> & {
@ -153,6 +160,13 @@ export type ModItem<P extends Phase> = {
export type ExternItem = { name: string }; export type ExternItem = { name: string };
export type GlobalItem<P extends Phase> = {
name: string;
type: Type<P>;
init: Expr<P>;
ty?: Ty;
};
export type ExprEmpty = { kind: "empty" }; export type ExprEmpty = { kind: "empty" };
export type ExprLet<P extends Phase> = { export type ExprLet<P extends Phase> = {
@ -634,6 +648,17 @@ export function superFoldItem<From extends Phase, To extends Phase>(
case "extern": { case "extern": {
return { ...item, kind: "extern" }; return { ...item, kind: "extern" };
} }
case "global": {
return {
...item,
kind: "global",
node: {
name: item.node.name,
type: folder.type(item.node.type),
init: folder.expr(item.node.init),
},
};
}
} }
} }

View file

@ -13,10 +13,10 @@ import { Crate, Built, Typecked } from "./ast";
import { Ids } from "./utils"; import { Ids } from "./utils";
const INPUT = ` const INPUT = `
extern mod std; global HELLO: I32 = 0_I32;
function main() = ( function main() = (
std.printlnInt(10000); HELLO = 1_I32;
); );
`; `;

View file

@ -12,6 +12,7 @@ export type DatalessToken =
| "import" | "import"
| "extern" | "extern"
| "mod" | "mod"
| "global"
| "(" | "("
| ")" | ")"
| "{" | "{"
@ -316,6 +317,7 @@ const KEYOWRDS: DatalessToken[] = [
"import", "import",
"extern", "extern",
"mod", "mod",
"global",
]; ];
const KEYWORD_SET = new Set<string>(KEYOWRDS); const KEYWORD_SET = new Set<string>(KEYOWRDS);

View file

@ -3,8 +3,10 @@ import {
Expr, Expr,
ExprBlock, ExprBlock,
FunctionDef, FunctionDef,
GlobalItem,
ImportDef, ImportDef,
Item, Item,
ItemId,
LoopId, LoopId,
Resolution, Resolution,
Ty, Ty,
@ -13,6 +15,7 @@ import {
Typecked, Typecked,
varUnreachable, varUnreachable,
} from "./ast"; } from "./ast";
import { printTy } from "./printer";
import { ComplexMap, encodeUtf8, unwrap } from "./utils"; import { ComplexMap, encodeUtf8, unwrap } from "./utils";
import * as wasm from "./wasm/defs"; import * as wasm from "./wasm/defs";
@ -25,10 +28,19 @@ const STRING_ABI: ArgRetAbi = STRING_TYPES;
const WASM_PAGE = 65536; const WASM_PAGE = 65536;
type Relocation = { const DUMMY_IDX = 9999999;
kind: "funccall";
instr: wasm.Instr & { func: wasm.FuncIdx }; type RelocationKind =
} & { res: Resolution }; | {
kind: "funccall";
instr: wasm.Instr & { func: wasm.FuncIdx };
}
| {
kind: "globalref";
instr: wasm.Instr & { imm: wasm.GlobalIdx };
};
type Relocation = RelocationKind & { res: Resolution };
type FuncOrImport = type FuncOrImport =
| { kind: "func"; idx: wasm.FuncIdx } | { kind: "func"; idx: wasm.FuncIdx }
@ -39,6 +51,7 @@ export type Context = {
funcTypes: ComplexMap<wasm.FuncType, wasm.TypeIdx>; funcTypes: ComplexMap<wasm.FuncType, wasm.TypeIdx>;
reservedHeapMemoryStart: number; reservedHeapMemoryStart: number;
funcIndices: ComplexMap<Resolution, FuncOrImport>; funcIndices: ComplexMap<Resolution, FuncOrImport>;
globalIndices: ComplexMap<Resolution, wasm.GlobalIdx>;
crates: Crate<Typecked>[]; crates: Crate<Typecked>[];
relocations: Relocation[]; relocations: Relocation[];
}; };
@ -89,6 +102,12 @@ function appendData(cx: Context, newData: Uint8Array): number {
} }
} }
function findItem(cx: Context, id: ItemId): Item<Typecked> {
return unwrap(
unwrap(cx.crates.find((crate) => crate.id === id.crateId)).itemsById.get(id)
);
}
export function lower(crates: Crate<Typecked>[]): wasm.Module { export function lower(crates: Crate<Typecked>[]): wasm.Module {
const mod: wasm.Module = { const mod: wasm.Module = {
types: [], types: [],
@ -118,6 +137,7 @@ export function lower(crates: Crate<Typecked>[]): wasm.Module {
mod, mod,
funcTypes: new ComplexMap(), funcTypes: new ComplexMap(),
funcIndices: new ComplexMap(), funcIndices: new ComplexMap(),
globalIndices: new ComplexMap(),
reservedHeapMemoryStart: 0, reservedHeapMemoryStart: 0,
crates, crates,
relocations: [], relocations: [],
@ -136,6 +156,17 @@ export function lower(crates: Crate<Typecked>[]): wasm.Module {
} }
case "mod": { case "mod": {
lowerMod(item.node.contents); lowerMod(item.node.contents);
break;
}
case "global": {
lowerGlobal(cx, item, item.node);
break;
}
case "extern":
case "type":
break;
default: {
const _: never = item;
} }
} }
}); });
@ -162,6 +193,20 @@ export function lower(crates: Crate<Typecked>[]): wasm.Module {
); );
} }
rel.instr.func = idx.kind === "func" ? offset + idx.idx : idx.idx; rel.instr.func = idx.kind === "func" ? offset + idx.idx : idx.idx;
break;
}
case "globalref": {
const idx = cx.globalIndices.get(rel.res);
if (idx === undefined) {
throw new Error(
`no global found for relocation '${JSON.stringify(rel.res)}'`
);
}
rel.instr.imm = idx;
break;
}
default: {
const _: never = rel;
} }
} }
}); });
@ -201,6 +246,43 @@ function lowerImport(
cx.funcIndices.set({ kind: "item", id: item.id }, { kind: "import", idx }); cx.funcIndices.set({ kind: "item", id: item.id }, { kind: "import", idx });
} }
function lowerGlobal(
cx: Context,
item: Item<Typecked>,
def: GlobalItem<Typecked>
) {
const globalIdx = cx.mod.globals.length;
let valtype: "i32" | "i64";
switch (def.init.ty.kind) {
case "i32":
valtype = "i32";
break;
case "int":
valtype = "i64";
break;
default:
throw new Error(`invalid global ty: ${printTy(def.init.ty)}`);
}
if (def.init.kind !== "literal" || def.init.value.kind !== "int") {
throw new Error(`invalid global init: ${JSON.stringify(def)}`);
}
const init: wasm.Instr = {
kind: `${valtype}.const`,
imm: def.init.value.value,
};
cx.mod.globals.push({
_name: mangleDefPath(item.defPath),
type: { type: valtype, mut: "var" },
init: [init],
});
cx.globalIndices.set({ kind: "item", id: item.id }, globalIdx);
}
type FuncContext = { type FuncContext = {
cx: Context; cx: Context;
item: Item<Typecked>; item: Item<Typecked>;
@ -304,7 +386,18 @@ function lowerExpr(
break; break;
} }
case "item": { case "item": {
throw new Error("cannot store to item"); const item = findItem(fcx.cx, res.id);
if (item.kind !== "global") {
throw new Error("cannot store to non-global item");
}
const instr: wasm.Instr = { kind: "global.set", imm: DUMMY_IDX };
const rel: Relocation = { kind: "globalref", instr, res };
fcx.cx.relocations.push(rel);
instrs.push(instr);
break;
} }
case "builtin": { case "builtin": {
throw new Error("cannot store to builtin"); throw new Error("cannot store to builtin");
@ -374,8 +467,23 @@ function lowerExpr(
loadVariable(instrs, location); loadVariable(instrs, location);
break; break;
} }
case "item": case "item": {
todo("item ident res"); const item = findItem(fcx.cx, res.id);
switch (item.kind) {
case "global": {
const instr: wasm.Instr = { kind: "global.get", imm: DUMMY_IDX };
const rel: Relocation = { kind: "globalref", instr, res };
instrs.push(instr);
fcx.cx.relocations.push(rel);
break;
}
default: {
todo("non-global item ident res");
}
}
break;
}
case "builtin": case "builtin":
switch (res.name) { switch (res.name) {
case "false": case "false":
@ -561,7 +669,7 @@ function lowerExpr(
} }
} }
const callInstr: wasm.Instr = { kind: "call", func: 9999999999 }; const callInstr: wasm.Instr = { kind: "call", func: DUMMY_IDX };
fcx.cx.relocations.push({ fcx.cx.relocations.push({
kind: "funccall", kind: "funccall",
instr: callInstr, instr: callInstr,
@ -865,7 +973,7 @@ function addRt(cx: Context, crates: Crate<Typecked>[]) {
const crate0 = unwrap(crates.find((crate) => crate.id === 0)); const crate0 = unwrap(crates.find((crate) => crate.id === 0));
const mainCall: wasm.Instr = { kind: "call", func: 9999999 }; const mainCall: wasm.Instr = { kind: "call", func: DUMMY_IDX };
cx.relocations.push({ cx.relocations.push({
kind: "funccall", kind: "funccall",
instr: mainCall, instr: mainCall,

View file

@ -29,6 +29,7 @@ import {
Parsed, Parsed,
ExternItem, ExternItem,
ItemId, ItemId,
GlobalItem,
} from "./ast"; } from "./ast";
import { CompilerError, Span, spanMerge } from "./error"; import { CompilerError, Span, spanMerge } from "./error";
import { BaseToken, Token, TokenIdent, TokenLitString } from "./lexer"; import { BaseToken, Token, TokenIdent, TokenLitString } from "./lexer";
@ -180,6 +181,24 @@ function parseItem(t: Token[]): [Token[], Item<Parsed>] {
}; };
return [t, { kind: "mod", node, span: name.span, id: ItemId.dummy() }]; return [t, { kind: "mod", node, span: name.span, id: ItemId.dummy() }];
} else if (tok.kind === "global") {
let name;
[t, name] = expectNext<TokenIdent>(t, "identifier");
[t] = expectNext(t, ":");
let type;
[t, type] = parseType(t);
[t] = expectNext(t, "=");
let init;
[t, init] = parseExpr(t);
[t] = expectNext(t, ";");
const node: GlobalItem<Parsed> = {
name: name.ident,
type,
init,
};
return [t, { kind: "global", node, span: name.span, id: ItemId.dummy() }];
} else { } else {
unexpectedToken(tok, "item"); unexpectedToken(tok, "item");
} }

View file

@ -24,7 +24,7 @@ function printStringLiteral(lit: StringLiteral): string {
} }
function printItem(item: Item<AnyPhase>): string { function printItem(item: Item<AnyPhase>): string {
const id = `/*${item.id}*/ `; const id = `/*${item.id.toString()}*/ `;
switch (item.kind) { switch (item.kind) {
case "function": { case "function": {
@ -42,6 +42,15 @@ function printItem(item: Item<AnyPhase>): string {
case "extern": { case "extern": {
return id + `extern mod ${item.node.name};`; return id + `extern mod ${item.node.name};`;
} }
case "global": {
return (
id +
`global ${item.node.name}: ${printType(item.node.type)} = ${printExpr(
item.node.init,
0
)};`
);
}
} }
} }
@ -207,7 +216,7 @@ function printRes(res: Resolution): string {
case "local": case "local":
return `#${res.index}`; return `#${res.index}`;
case "item": case "item":
return `#G${res.id}`; return `#I${res.id.toString()}`;
case "builtin": { case "builtin": {
return `#B`; return `#B`;
} }

View file

@ -120,6 +120,7 @@ export function typeck(
otherCrates: Crate<Typecked>[] otherCrates: Crate<Typecked>[]
): Crate<Typecked> { ): Crate<Typecked> {
const itemTys = new ComplexMap<ItemId, Ty | null>(); const itemTys = new ComplexMap<ItemId, Ty | null>();
function typeOfItem(itemId: ItemId, cause: Span): Ty { function typeOfItem(itemId: ItemId, cause: Span): Ty {
if (itemId.crateId !== ast.id) { if (itemId.crateId !== ast.id) {
console.log(otherCrates); console.log(otherCrates);
@ -132,6 +133,7 @@ export function typeck(
case "function": case "function":
case "import": case "import":
case "type": case "type":
case "global":
return item.node.ty!; return item.node.ty!;
case "mod": { case "mod": {
throw new CompilerError( throw new CompilerError(
@ -203,6 +205,11 @@ export function typeck(
cause cause
); );
} }
case "global": {
const ty = lowerAstTy(item.node.type);
itemTys.set(item.id, ty);
return ty;
}
} }
} }
@ -227,13 +234,24 @@ export function typeck(
); );
} }
function findItem(itemId: ItemId): Item<Resolved> {
if (itemId.crateId === ast.id) {
return unwrap(ast.itemsById.get(itemId));
}
return unwrap(
unwrap(
otherCrates.find((crate) => crate.id === itemId.crateId)
).itemsById.get(itemId)
);
}
const checker: Folder<Resolved, Typecked> = { const checker: Folder<Resolved, Typecked> = {
...mkDefaultFolder(), ...mkDefaultFolder(),
itemInner(item: Item<Resolved>): Item<Typecked> { itemInner(item: Item<Resolved>): Item<Typecked> {
switch (item.kind) { switch (item.kind) {
case "function": { case "function": {
const fnTy = typeOfItem(item.id, item.span) as TyFn; const fnTy = typeOfItem(item.id, item.span) as TyFn;
const body = checkBody(item.node.body, fnTy, typeOfItem); const body = checkBody(item.node.body, fnTy, typeOfItem, findItem);
const returnType = item.node.returnType && { const returnType = item.node.returnType && {
...item.node.returnType, ...item.node.returnType,
@ -349,6 +367,30 @@ export function typeck(
node: { ...item.node }, node: { ...item.node },
}; };
} }
case "global": {
const ty = typeOfItem(item.id, item.span);
const { init } = item.node;
if (init.kind !== "literal" || init.value.kind !== "int") {
throw new CompilerError(
"globals must be initialized with an integer literal",
init.span
);
}
const initTy = init.value.type === "I32" ? TY_I32 : TY_INT;
const infcx = new InferContext();
infcx.assign(ty, initTy, init.span);
return {
...item,
node: {
...item.node,
ty,
init: { ...init, ty },
},
};
}
} }
}, },
expr(_expr) { expr(_expr) {
@ -560,7 +602,8 @@ export class InferContext {
export function checkBody( export function checkBody(
body: Expr<Resolved>, body: Expr<Resolved>,
fnTy: TyFn, fnTy: TyFn,
typeOfItem: (itemId: ItemId, cause: Span) => Ty typeOfItem: (itemId: ItemId, cause: Span) => Ty,
findItem: (itemId: ItemId) => Item<Resolved>
): Expr<Typecked> { ): Expr<Typecked> {
const localTys = [...fnTy.params]; const localTys = [...fnTy.params];
const loopState: { hasBreak: boolean; loopId: LoopId }[] = []; const loopState: { hasBreak: boolean; loopId: LoopId }[] = [];
@ -644,11 +687,26 @@ export function checkBody(
infcx.assign(lhs.ty, rhs.ty, expr.span); infcx.assign(lhs.ty, rhs.ty, expr.span);
switch (lhs.kind) { switch (lhs.kind) {
case "ident": case "ident": {
if (lhs.value.res.kind !== "local") { const { res } = lhs.value;
throw new CompilerError("cannot assign to items", expr.span); switch (res.kind) {
case "local":
break;
case "item": {
const item = findItem(res.id);
if (item.kind !== "global") {
throw new CompilerError("cannot assign to item", expr.span);
}
break;
}
case "builtin":
throw new CompilerError(
"cannot assign to builtins",
expr.span
);
} }
break; break;
}
default: { default: {
throw new CompilerError( throw new CompilerError(
"invalid left-hand side of assignment", "invalid left-hand side of assignment",

View file

@ -57,3 +57,9 @@ function println(s: String) = (
print(s); print(s);
print("\n"); print("\n");
); );
mod alloc (
function allocateItem(size: I32): I32 = (
);
)

View file

@ -1,69 +1,5 @@
mod owo (); extern mod std;
function main() = ( function main() = (
prIntln(0); std.printInt(10);
prIntln(1); );
prIntln(9);
prIntln(2352353);
prIntln(100);
);
function prIntln(x: Int) = (
prInt(x);
print("\n");
);
function stringForDigit(x: Int): String =
if x == 0 then "0"
else if x == 1 then "1"
else if x == 2 then "2"
else if x == 3 then "3"
else if x == 4 then "4"
else if x == 5 then "5"
else if x == 6 then "6"
else if x == 7 then "7"
else if x == 8 then "8"
else if x == 9 then "9"
else trap();
function log10(x: Int): Int = (
let i = 0;
loop (
if x < 10 then break;
i = i + 1;
x = x / 10;
);
i
);
function pow(base: Int, exp: Int): Int = (
let acc = 1;
loop (
if exp == 0 then break;
acc = acc * base;
exp = exp - 1;
);
acc
);
function prInt(x: Int) = (
let mag = log10(x);
loop (
if mag == 0 then break;
let base = pow(10, mag);
let digit = x / base;
print(stringForDigit(digit));
x = x % base;
mag = mag - 1;
);
print(stringForDigit(x % 10));
);
function println(s: String) = (
print(s);
print("\n");
);