inline assembly :3

This commit is contained in:
nora 2023-12-15 22:31:13 +01:00
parent 3ab116d7f0
commit ba5d41674c
17 changed files with 448 additions and 45 deletions

View file

@ -28,12 +28,10 @@ import {
} from "../ast";
import { CompilerError, ErrorEmitted, Span, unreachable } from "../error";
import { printTy } from "../printer";
import { INSTRS, Instr, VALTYPES, ValType } from "../wasm/defs";
import { TypeckCtx, emitError, mkTyFn, tyError, tyErrorFrom } from "./base";
import { InferContext } from "./infer";
import {
lowerAstTy,
typeOfItem,
} from "./item";
import { lowerAstTy, typeOfItem } from "./item";
export function exprError(err: ErrorEmitted, span: Span): Expr<Typecked> {
return {
@ -129,6 +127,15 @@ export function checkBody(
checkExpr: () => unreachable(),
};
if (
body.kind === "call" &&
body.lhs.kind === "ident" &&
body.lhs.value.res.kind === "builtin" &&
body.lhs.value.res.name === "___asm"
) {
return checkInlineAsm(cx, body, fnTy.returnTy);
}
const checker: Folder<Resolved, Typecked> = {
...mkDefaultFolder(),
expr(expr): Expr<Typecked> {
@ -503,6 +510,9 @@ export function checkBody(
return { ...expr, fields, ty };
}
case "asm": {
unreachable("asm expression doesn't exist before type checking");
}
case "error": {
return { ...expr, ty: tyErrorFrom(expr) };
}
@ -530,6 +540,90 @@ export function checkBody(
return resolved;
}
function checkInlineAsm(
cx: TypeckCtx,
body: Expr<Resolved> & ExprCall<Resolved>,
retTy: Ty,
): Expr<Typecked> {
const err = (msg: string, span: Span): Expr<Typecked> =>
exprError(emitError(cx, new CompilerError(msg, span)), span);
const args = body.args;
if (
args.length < 1 ||
args[0].kind !== "call" ||
args[0].lhs.kind !== "ident" ||
args[0].lhs.value.res.kind !== "builtin" ||
args[0].lhs.value.res.name !== "__locals"
) {
return err(
"inline assembly must have __locals() as first argument",
body.span,
);
}
const locals: ValType[] = [];
for (const local of args[0].args) {
const isValtype = (s: string): s is ValType =>
VALTYPES.includes(s as ValType);
if (
local.kind !== "literal" ||
local.value.kind !== "str" ||
!isValtype(local.value.value)
) {
return err(
"inline assembly local must be string literal of value type",
local.span,
);
}
locals.push(local.value.value);
}
const instructions: Instr[] = [];
for (const expr of args.slice(1)) {
if (expr.kind !== "literal" || expr.value.kind !== "str") {
return err(
"inline assembly instruction must be string literal with instruction",
expr.span,
);
}
const text = expr.value.value;
const parts = text.split(" ");
const imms = parts.slice(1);
const wasmInstr = INSTRS.find((instrVal) => instrVal.name === parts[0]);
if (!wasmInstr) {
return err(`unknown instruction: ${parts[0]}`, expr.span);
}
if (wasmInstr.immediates === "select") {
throw new Error("todo: select");
} else if (wasmInstr.immediates === "memarg") {
throw new Error("todo: memarg");
} else {
if (imms.length !== wasmInstr.immediates.length) {
return err(
`mismatched immediate lengths, expected ${wasmInstr.immediates.length}, got ${imms.length}`,
expr.span,
);
}
if (wasmInstr.immediates.length > 1) {
throw new Error("todo: immediates");
}
if (wasmInstr.immediates.length === 0) {
instructions.push({ kind: wasmInstr.name } as Instr);
} else {
instructions.push({
kind: wasmInstr.name,
imm: Number(imms[0]),
} as Instr);
}
}
}
return { kind: "asm", locals, ty: retTy, instructions, span: body.span };
}
function checkLValue(cx: TypeckCtx, expr: Expr<Typecked>) {
switch (expr.kind) {
case "ident":
@ -641,21 +735,19 @@ function checkCall(
fcx: FuncCtx,
expr: ExprCall<Resolved> & Expr<Resolved>,
): Expr<Typecked> {
if (
expr.lhs.kind === "ident" &&
expr.lhs.value.res.kind === "builtin" &&
expr.lhs.value.res.name === "___transmute"
) {
const ty = fcx.infcx.newVar();
const args = expr.args.map((arg) => fcx.checkExpr(arg));
const ret: Expr<Typecked> = {
...expr,
lhs: { ...expr.lhs, ty: TY_UNIT },
args,
ty,
};
if (expr.lhs.kind === "ident" && expr.lhs.value.res.kind === "builtin") {
if (expr.lhs.value.res.name === "___transmute") {
const ty = fcx.infcx.newVar();
const args = expr.args.map((arg) => fcx.checkExpr(arg));
const ret: Expr<Typecked> = {
...expr,
lhs: { ...expr.lhs, ty: TY_UNIT },
args,
ty,
};
return ret;
return ret;
}
}
const lhs = fcx.checkExpr(expr.lhs);