mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-14 16:35:03 +01:00
inline assembly :3
This commit is contained in:
parent
3ab116d7f0
commit
ba5d41674c
17 changed files with 448 additions and 45 deletions
13
src/ast.ts
13
src/ast.ts
|
|
@ -1,6 +1,7 @@
|
||||||
import { ErrorEmitted, LoadedFile, Span, unreachable } from "./error";
|
import { ErrorEmitted, LoadedFile, Span, unreachable } from "./error";
|
||||||
import { LitIntType } from "./lexer";
|
import { LitIntType } from "./lexer";
|
||||||
import { ComplexMap } from "./utils";
|
import { ComplexMap } from "./utils";
|
||||||
|
import { Instr, ValType } from "./wasm/defs";
|
||||||
|
|
||||||
export type Phase = {
|
export type Phase = {
|
||||||
res: unknown;
|
res: unknown;
|
||||||
|
|
@ -308,6 +309,12 @@ export type ExprTupleLiteral<P extends Phase> = {
|
||||||
fields: Expr<P>[];
|
fields: Expr<P>[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ExprInlineAsm = {
|
||||||
|
kind: "asm";
|
||||||
|
locals: ValType[];
|
||||||
|
instructions: Instr[];
|
||||||
|
};
|
||||||
|
|
||||||
export type ExprError = {
|
export type ExprError = {
|
||||||
kind: "error";
|
kind: "error";
|
||||||
err: ErrorEmitted;
|
err: ErrorEmitted;
|
||||||
|
|
@ -330,6 +337,7 @@ export type ExprKind<P extends Phase> =
|
||||||
| ExprBreak
|
| ExprBreak
|
||||||
| ExprStructLiteral<P>
|
| ExprStructLiteral<P>
|
||||||
| ExprTupleLiteral<P>
|
| ExprTupleLiteral<P>
|
||||||
|
| ExprInlineAsm
|
||||||
| ExprError;
|
| ExprError;
|
||||||
|
|
||||||
export type Expr<P extends Phase> = ExprKind<P> & {
|
export type Expr<P extends Phase> = ExprKind<P> & {
|
||||||
|
|
@ -485,6 +493,8 @@ export const BUILTINS = [
|
||||||
"__memory_grow",
|
"__memory_grow",
|
||||||
"__i32_extend_to_i64_u",
|
"__i32_extend_to_i64_u",
|
||||||
"___transmute",
|
"___transmute",
|
||||||
|
"___asm",
|
||||||
|
"__locals",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type BuiltinName = (typeof BUILTINS)[number];
|
export type BuiltinName = (typeof BUILTINS)[number];
|
||||||
|
|
@ -920,6 +930,9 @@ export function superFoldExpr<From extends Phase, To extends Phase>(
|
||||||
fields: expr.fields.map(folder.expr.bind(folder)),
|
fields: expr.fields.map(folder.expr.bind(folder)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
case "asm": {
|
||||||
|
return { ...expr };
|
||||||
|
}
|
||||||
case "error": {
|
case "error": {
|
||||||
return { ...expr };
|
return { ...expr };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,9 +118,7 @@ function appendData(cx: Context, newData: Uint8Array): number {
|
||||||
|
|
||||||
const KNOWN_DEF_PATHS = [ALLOCATE_ITEM, DEALLOCATE_ITEM];
|
const KNOWN_DEF_PATHS = [ALLOCATE_ITEM, DEALLOCATE_ITEM];
|
||||||
|
|
||||||
function getKnownDefPaths(
|
function getKnownDefPaths(pkgs: Pkg<Typecked>[]): ComplexMap<string[], ItemId> {
|
||||||
pkgs: Pkg<Typecked>[],
|
|
||||||
): ComplexMap<string[], ItemId> {
|
|
||||||
const knows = new ComplexMap<string[], ItemId>();
|
const knows = new ComplexMap<string[], ItemId>();
|
||||||
|
|
||||||
const folder: Folder<Typecked, Typecked> = {
|
const folder: Folder<Typecked, Typecked> = {
|
||||||
|
|
@ -145,9 +143,7 @@ function getKnownDefPaths(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pkgs.forEach((pkg) =>
|
pkgs.forEach((pkg) => pkg.rootItems.forEach((item) => folder.item(item)));
|
||||||
pkg.rootItems.forEach((item) => folder.item(item)),
|
|
||||||
);
|
|
||||||
|
|
||||||
return knows;
|
return knows;
|
||||||
}
|
}
|
||||||
|
|
@ -380,7 +376,12 @@ function lowerFunc(cx: Context, func: ItemFunction<Typecked>) {
|
||||||
scratchLocals: new Map(),
|
scratchLocals: new Map(),
|
||||||
};
|
};
|
||||||
|
|
||||||
lowerExpr(fcx, wasmFunc.body, fcx.func.body);
|
const body = fcx.func.body;
|
||||||
|
if (body.kind === "asm") {
|
||||||
|
fcx.wasm.locals = body.locals;
|
||||||
|
fcx.wasm.body = body.instructions;
|
||||||
|
} else {
|
||||||
|
lowerExpr(fcx, wasmFunc.body, body);
|
||||||
|
|
||||||
paramLocations.forEach((local) => {
|
paramLocations.forEach((local) => {
|
||||||
const refcount = needsRefcount(local.ty);
|
const refcount = needsRefcount(local.ty);
|
||||||
|
|
@ -390,6 +391,7 @@ function lowerFunc(cx: Context, func: ItemFunction<Typecked>) {
|
||||||
subRefcount(fcx, wasmFunc.body, refcount);
|
subRefcount(fcx, wasmFunc.body, refcount);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const idx = fcx.cx.mod.funcs.length;
|
const idx = fcx.cx.mod.funcs.length;
|
||||||
fcx.cx.mod.funcs.push(wasmFunc);
|
fcx.cx.mod.funcs.push(wasmFunc);
|
||||||
|
|
@ -1084,6 +1086,9 @@ function lowerExpr(
|
||||||
expr.fields.forEach((field) => lowerExpr(fcx, instrs, field));
|
expr.fields.forEach((field) => lowerExpr(fcx, instrs, field));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "asm": {
|
||||||
|
unreachable("asm");
|
||||||
|
}
|
||||||
case "error":
|
case "error":
|
||||||
unreachable("codegen should never see errors");
|
unreachable("codegen should never see errors");
|
||||||
default: {
|
default: {
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,12 @@ function printExpr(expr: Expr<AnyPhase>, indent: number): string {
|
||||||
.map((expr) => printExpr(expr, indent))
|
.map((expr) => printExpr(expr, indent))
|
||||||
.join(", ")})`;
|
.join(", ")})`;
|
||||||
}
|
}
|
||||||
|
case "asm": {
|
||||||
|
return `___asm(___locals(${expr.locals
|
||||||
|
.map((valty) => `"${valty}"`)
|
||||||
|
// object object
|
||||||
|
.join(", ")}), ${expr.instructions.join(", ")})`;
|
||||||
|
}
|
||||||
case "error":
|
case "error":
|
||||||
return "<ERROR>";
|
return "<ERROR>";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,10 @@ import {
|
||||||
} from "../ast";
|
} from "../ast";
|
||||||
import { CompilerError, ErrorEmitted, Span, unreachable } from "../error";
|
import { CompilerError, ErrorEmitted, Span, unreachable } from "../error";
|
||||||
import { printTy } from "../printer";
|
import { printTy } from "../printer";
|
||||||
|
import { INSTRS, Instr, VALTYPES, ValType } from "../wasm/defs";
|
||||||
import { TypeckCtx, emitError, mkTyFn, tyError, tyErrorFrom } from "./base";
|
import { TypeckCtx, emitError, mkTyFn, tyError, tyErrorFrom } from "./base";
|
||||||
import { InferContext } from "./infer";
|
import { InferContext } from "./infer";
|
||||||
import {
|
import { lowerAstTy, typeOfItem } from "./item";
|
||||||
lowerAstTy,
|
|
||||||
typeOfItem,
|
|
||||||
} from "./item";
|
|
||||||
|
|
||||||
export function exprError(err: ErrorEmitted, span: Span): Expr<Typecked> {
|
export function exprError(err: ErrorEmitted, span: Span): Expr<Typecked> {
|
||||||
return {
|
return {
|
||||||
|
|
@ -129,6 +127,15 @@ export function checkBody(
|
||||||
checkExpr: () => unreachable(),
|
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> = {
|
const checker: Folder<Resolved, Typecked> = {
|
||||||
...mkDefaultFolder(),
|
...mkDefaultFolder(),
|
||||||
expr(expr): Expr<Typecked> {
|
expr(expr): Expr<Typecked> {
|
||||||
|
|
@ -503,6 +510,9 @@ export function checkBody(
|
||||||
|
|
||||||
return { ...expr, fields, ty };
|
return { ...expr, fields, ty };
|
||||||
}
|
}
|
||||||
|
case "asm": {
|
||||||
|
unreachable("asm expression doesn't exist before type checking");
|
||||||
|
}
|
||||||
case "error": {
|
case "error": {
|
||||||
return { ...expr, ty: tyErrorFrom(expr) };
|
return { ...expr, ty: tyErrorFrom(expr) };
|
||||||
}
|
}
|
||||||
|
|
@ -530,6 +540,90 @@ export function checkBody(
|
||||||
return resolved;
|
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>) {
|
function checkLValue(cx: TypeckCtx, expr: Expr<Typecked>) {
|
||||||
switch (expr.kind) {
|
switch (expr.kind) {
|
||||||
case "ident":
|
case "ident":
|
||||||
|
|
@ -641,11 +735,8 @@ function checkCall(
|
||||||
fcx: FuncCtx,
|
fcx: FuncCtx,
|
||||||
expr: ExprCall<Resolved> & Expr<Resolved>,
|
expr: ExprCall<Resolved> & Expr<Resolved>,
|
||||||
): Expr<Typecked> {
|
): Expr<Typecked> {
|
||||||
if (
|
if (expr.lhs.kind === "ident" && expr.lhs.value.res.kind === "builtin") {
|
||||||
expr.lhs.kind === "ident" &&
|
if (expr.lhs.value.res.name === "___transmute") {
|
||||||
expr.lhs.value.res.kind === "builtin" &&
|
|
||||||
expr.lhs.value.res.name === "___transmute"
|
|
||||||
) {
|
|
||||||
const ty = fcx.infcx.newVar();
|
const ty = fcx.infcx.newVar();
|
||||||
const args = expr.args.map((arg) => fcx.checkExpr(arg));
|
const args = expr.args.map((arg) => fcx.checkExpr(arg));
|
||||||
const ret: Expr<Typecked> = {
|
const ret: Expr<Typecked> = {
|
||||||
|
|
@ -657,6 +748,7 @@ function checkCall(
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const lhs = fcx.checkExpr(expr.lhs);
|
const lhs = fcx.checkExpr(expr.lhs);
|
||||||
lhs.ty = fcx.infcx.resolveIfPossible(lhs.ty);
|
lhs.ty = fcx.infcx.resolveIfPossible(lhs.ty);
|
||||||
|
|
|
||||||
200
src/wasm/defs.ts
200
src/wasm/defs.ts
|
|
@ -19,6 +19,15 @@ export type Vectype = "v128";
|
||||||
export type Reftype = "funcref" | "externref";
|
export type Reftype = "funcref" | "externref";
|
||||||
|
|
||||||
export type ValType = Numtype | Vectype | Reftype;
|
export type ValType = Numtype | Vectype | Reftype;
|
||||||
|
export const VALTYPES: ValType[] = [
|
||||||
|
"i32",
|
||||||
|
"i64",
|
||||||
|
"f32",
|
||||||
|
"f64",
|
||||||
|
"v128",
|
||||||
|
"funcref",
|
||||||
|
"externref",
|
||||||
|
];
|
||||||
|
|
||||||
export type ResultType = ValType[];
|
export type ResultType = ValType[];
|
||||||
|
|
||||||
|
|
@ -66,11 +75,25 @@ export type Externtype =
|
||||||
|
|
||||||
// instructions
|
// instructions
|
||||||
|
|
||||||
|
// Value representations of the types for the assembler
|
||||||
|
export type InstrValue = {
|
||||||
|
name: Instr["kind"];
|
||||||
|
immediates: ImmediateValue[] | "select" | "memarg";
|
||||||
|
};
|
||||||
|
type ImmediateValue = "i32" | "i64" | "f32" | "f64" | "refkind";
|
||||||
|
|
||||||
|
const ins = (name: Instr["kind"], immediates: InstrValue["immediates"]) => ({
|
||||||
|
name,
|
||||||
|
immediates,
|
||||||
|
});
|
||||||
|
|
||||||
// . numeric
|
// . numeric
|
||||||
|
|
||||||
export type BitWidth = "32" | "64";
|
export type BitWidth = "32" | "64";
|
||||||
|
const BIT_WIDTHS: BitWidth[] = ["32", "64"];
|
||||||
|
|
||||||
export type Sign = "u" | "s";
|
export type Sign = "u" | "s";
|
||||||
|
const SIGNS: Sign[] = ["u", "s"];
|
||||||
|
|
||||||
export type NumericInstr =
|
export type NumericInstr =
|
||||||
| { kind: "i32.const"; imm: bigint }
|
| { kind: "i32.const"; imm: bigint }
|
||||||
|
|
@ -86,7 +109,7 @@ export type NumericInstr =
|
||||||
| { kind: `f${BitWidth}.${FRelOp}` }
|
| { kind: `f${BitWidth}.${FRelOp}` }
|
||||||
| { kind: `i${BitWidth}.extend8_s` }
|
| { kind: `i${BitWidth}.extend8_s` }
|
||||||
| { kind: `i${BitWidth}.extend16_s` }
|
| { kind: `i${BitWidth}.extend16_s` }
|
||||||
| { kind: `i64.extend32_s` }
|
| { kind: "i64.extend32_s" }
|
||||||
| { kind: "i32.wrap_i64" }
|
| { kind: "i32.wrap_i64" }
|
||||||
| { kind: `i64.extend_i32_${Sign}` }
|
| { kind: `i64.extend_i32_${Sign}` }
|
||||||
| { kind: `i${BitWidth}.trunc_f${BitWidth}_${Sign}` }
|
| { kind: `i${BitWidth}.trunc_f${BitWidth}_${Sign}` }
|
||||||
|
|
@ -98,6 +121,7 @@ export type NumericInstr =
|
||||||
| { kind: "f32.reinterpret_i32" | "f64.reinterpret_i64" };
|
| { kind: "f32.reinterpret_i32" | "f64.reinterpret_i64" };
|
||||||
|
|
||||||
export type IUnOp = "clz" | "ctz" | "popcnt";
|
export type IUnOp = "clz" | "ctz" | "popcnt";
|
||||||
|
const I_UN_OPS: IUnOp[] = ["clz", "ctz", "popcnt"];
|
||||||
|
|
||||||
export type IBinOp =
|
export type IBinOp =
|
||||||
| "add"
|
| "add"
|
||||||
|
|
@ -112,19 +136,47 @@ export type IBinOp =
|
||||||
| `shr_${Sign}`
|
| `shr_${Sign}`
|
||||||
| "rotl"
|
| "rotl"
|
||||||
| "rotr";
|
| "rotr";
|
||||||
|
const I_BIN_OPS: IBinOp[] = [
|
||||||
|
"add",
|
||||||
|
"sub",
|
||||||
|
"mul",
|
||||||
|
"and",
|
||||||
|
"or",
|
||||||
|
"xor",
|
||||||
|
"shl",
|
||||||
|
"rotl",
|
||||||
|
"rotr",
|
||||||
|
...SIGNS.flatMap((sign): IBinOp[] => [
|
||||||
|
`div_${sign}`,
|
||||||
|
`rem_${sign}`,
|
||||||
|
`shr_${sign}`,
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
export type FUnOp =
|
const F_UN_OPS = [
|
||||||
| "abs"
|
"abs",
|
||||||
| "neg"
|
"neg",
|
||||||
| "sqrt"
|
"sqrt",
|
||||||
| "ceil"
|
"ceil",
|
||||||
| "floor"
|
"floor",
|
||||||
| "trunc"
|
"trunc",
|
||||||
| "nearest";
|
"nearest",
|
||||||
|
] as const;
|
||||||
|
export type FUnOp = (typeof F_UN_OPS)[number];
|
||||||
|
|
||||||
export type FBinOp = "add" | "sub" | "mul" | "div" | "min" | "max" | "copysign";
|
const F_BIN_OPS = [
|
||||||
|
"add",
|
||||||
|
"sub",
|
||||||
|
"mul",
|
||||||
|
"div",
|
||||||
|
"min",
|
||||||
|
"max",
|
||||||
|
"copysign",
|
||||||
|
] as const;
|
||||||
|
export type FBinOp = (typeof F_BIN_OPS)[number];
|
||||||
|
|
||||||
export type ITestOp = "eqz";
|
const I_TEST_OPS = ["eqz"] as const;
|
||||||
|
export type ITestOp = (typeof I_TEST_OPS)[number];
|
||||||
|
|
||||||
export type IRelOp =
|
export type IRelOp =
|
||||||
| "eq"
|
| "eq"
|
||||||
|
|
@ -133,12 +185,62 @@ export type IRelOp =
|
||||||
| `gt_${Sign}`
|
| `gt_${Sign}`
|
||||||
| `le_${Sign}`
|
| `le_${Sign}`
|
||||||
| `ge_${Sign}`;
|
| `ge_${Sign}`;
|
||||||
|
const I_REL_OPS: IRelOp[] = [
|
||||||
|
"eq",
|
||||||
|
"ne",
|
||||||
|
...SIGNS.flatMap((sign): IRelOp[] => [
|
||||||
|
`lt_${sign}`,
|
||||||
|
`gt_${sign}`,
|
||||||
|
`le_${sign}`,
|
||||||
|
`ge_${sign}`,
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
export type FRelOp = "eq" | "ne" | "lt" | "gt" | "le" | "ge";
|
const F_REL_OPS = ["eq", "ne", "lt", "gt", "le", "ge"] as const;
|
||||||
|
export type FRelOp = (typeof F_REL_OPS)[number];
|
||||||
|
|
||||||
|
const NO_IMM_NUMERIC_INSTRS: NumericInstr["kind"][] = [
|
||||||
|
...BIT_WIDTHS.flatMap((bitWidth): NumericInstr["kind"][] => [
|
||||||
|
...I_UN_OPS.map((op): NumericInstr["kind"] => `i${bitWidth}.${op}`),
|
||||||
|
...F_UN_OPS.map((op): NumericInstr["kind"] => `f${bitWidth}.${op}`),
|
||||||
|
...I_BIN_OPS.map((op): NumericInstr["kind"] => `i${bitWidth}.${op}`),
|
||||||
|
...F_BIN_OPS.map((op): NumericInstr["kind"] => `f${bitWidth}.${op}`),
|
||||||
|
...I_TEST_OPS.map((op): NumericInstr["kind"] => `i${bitWidth}.${op}`),
|
||||||
|
...I_REL_OPS.map((op): NumericInstr["kind"] => `i${bitWidth}.${op}`),
|
||||||
|
...F_REL_OPS.map((op): NumericInstr["kind"] => `f${bitWidth}.${op}`),
|
||||||
|
`i${bitWidth}.extend8_s`,
|
||||||
|
`i${bitWidth}.extend16_s`,
|
||||||
|
...BIT_WIDTHS.flatMap((bitWidth2): NumericInstr["kind"][] =>
|
||||||
|
SIGNS.flatMap((sign): NumericInstr["kind"][] => [
|
||||||
|
`i${bitWidth}.trunc_f${bitWidth2}_${sign}`,
|
||||||
|
`i${bitWidth}.trunc_sat_f${bitWidth2}_${sign}`,
|
||||||
|
`f${bitWidth}.convert_i${bitWidth2}_${sign}`,
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
"i64.extend32_s",
|
||||||
|
"i32.wrap_i64",
|
||||||
|
...SIGNS.flatMap((sign): NumericInstr["kind"] => `i64.extend_i32_${sign}`),
|
||||||
|
"f32.demote_f64",
|
||||||
|
"f64.promote_f32",
|
||||||
|
"i32.reinterpret_f32",
|
||||||
|
"i64.reinterpret_f64",
|
||||||
|
"f32.reinterpret_i32",
|
||||||
|
"f64.reinterpret_i64",
|
||||||
|
];
|
||||||
|
const IMM_NUMERIC_INSTRS: InstrValue[] = (
|
||||||
|
["i32", "i64", "f32", "f64"] as const
|
||||||
|
).map((type) => ins(`${type}.const`, [type]));
|
||||||
|
|
||||||
|
const NUMERIC_INSTRS: InstrValue[] = [
|
||||||
|
...NO_IMM_NUMERIC_INSTRS.map((instr) => ins(instr, [])),
|
||||||
|
...IMM_NUMERIC_INSTRS,
|
||||||
|
];
|
||||||
|
|
||||||
// . vectors
|
// . vectors
|
||||||
|
|
||||||
export type VectorInstr = never;
|
export type VectorInstr = never;
|
||||||
|
const VECTOR_INSTRS: InstrValue[] = [];
|
||||||
|
|
||||||
// . reference
|
// . reference
|
||||||
|
|
||||||
|
|
@ -146,12 +248,21 @@ export type ReferenceInstr =
|
||||||
| { kind: "ref.null"; imm: Reftype }
|
| { kind: "ref.null"; imm: Reftype }
|
||||||
| { kind: "ref.is_null" }
|
| { kind: "ref.is_null" }
|
||||||
| { kind: "ref.func"; imm: FuncIdx };
|
| { kind: "ref.func"; imm: FuncIdx };
|
||||||
|
const REFERENCE_INSTRS: InstrValue[] = [
|
||||||
|
ins("ref.null", ["refkind"]),
|
||||||
|
ins("ref.is_null", []),
|
||||||
|
ins("ref.func", ["i32"]),
|
||||||
|
];
|
||||||
|
|
||||||
// . parametric
|
// . parametric
|
||||||
|
|
||||||
export type ParametricInstr =
|
export type ParametricInstr =
|
||||||
| { kind: "drop" }
|
| { kind: "drop" }
|
||||||
| { kind: "select"; type?: ValType[] };
|
| { kind: "select"; type?: ValType[] };
|
||||||
|
const PARAMETRIC_INSTRS: InstrValue[] = [
|
||||||
|
ins("drop", []),
|
||||||
|
ins("select", "select"),
|
||||||
|
];
|
||||||
|
|
||||||
// . variable
|
// . variable
|
||||||
|
|
||||||
|
|
@ -164,6 +275,12 @@ export type VariableInstr =
|
||||||
kind: `global.${"get" | "set"}`;
|
kind: `global.${"get" | "set"}`;
|
||||||
imm: LocalIdx;
|
imm: LocalIdx;
|
||||||
};
|
};
|
||||||
|
const VARIABLE_INSTR: InstrValue[] = [
|
||||||
|
...(["get", "set", "tee"] as const).map((kind) =>
|
||||||
|
ins(`local.${kind}`, ["i32"]),
|
||||||
|
),
|
||||||
|
...(["get", "set"] as const).map((kind) => ins(`global.${kind}`, ["i32"])),
|
||||||
|
];
|
||||||
|
|
||||||
// . table
|
// . table
|
||||||
|
|
||||||
|
|
@ -186,6 +303,14 @@ export type TableInstr =
|
||||||
kind: "elem.drop";
|
kind: "elem.drop";
|
||||||
imm: ElemIdx;
|
imm: ElemIdx;
|
||||||
};
|
};
|
||||||
|
const TABLE_INSTRS: InstrValue[] = [
|
||||||
|
...(["get", "set", "size", "grow", "fill"] as const).map((kind) =>
|
||||||
|
ins(`table.${kind}`, ["i32"]),
|
||||||
|
),
|
||||||
|
ins("table.copy", ["i32", "i32"]),
|
||||||
|
ins("table.init", ["i32", "i32"]),
|
||||||
|
ins("elem.drop", ["i32"]),
|
||||||
|
];
|
||||||
|
|
||||||
// . memory
|
// . memory
|
||||||
|
|
||||||
|
|
@ -197,6 +322,18 @@ export type MemArg = {
|
||||||
export type SimpleStoreKind = `${`${"i" | "f"}${BitWidth}` | "v128"}.${
|
export type SimpleStoreKind = `${`${"i" | "f"}${BitWidth}` | "v128"}.${
|
||||||
| "load"
|
| "load"
|
||||||
| "store"}`;
|
| "store"}`;
|
||||||
|
const SIMPLE_STORES: SimpleStoreKind[] = [
|
||||||
|
"i32.load",
|
||||||
|
"i32.store",
|
||||||
|
"f32.load",
|
||||||
|
"f32.store",
|
||||||
|
"i64.load",
|
||||||
|
"i64.store",
|
||||||
|
"f64.load",
|
||||||
|
"f64.store",
|
||||||
|
"v128.load",
|
||||||
|
"v128.store",
|
||||||
|
];
|
||||||
|
|
||||||
export type MemoryInstr =
|
export type MemoryInstr =
|
||||||
| {
|
| {
|
||||||
|
|
@ -221,6 +358,35 @@ export type MemoryInstr =
|
||||||
imm: DataIdx;
|
imm: DataIdx;
|
||||||
}
|
}
|
||||||
| { kind: "data.drop"; imm: DataIdx };
|
| { kind: "data.drop"; imm: DataIdx };
|
||||||
|
const MEMORY_INSTRS_WITH_MEMARG: MemoryInstr["kind"][] = [
|
||||||
|
...SIMPLE_STORES,
|
||||||
|
"i32.load8_u",
|
||||||
|
"i32.load8_s",
|
||||||
|
"i32.load16_u",
|
||||||
|
"i32.load16_s",
|
||||||
|
"i64.load8_u",
|
||||||
|
"i64.load8_s",
|
||||||
|
"i64.load16_u",
|
||||||
|
"i64.load16_s",
|
||||||
|
//
|
||||||
|
"i64.load32_u",
|
||||||
|
"i64.load32_s",
|
||||||
|
//
|
||||||
|
"i32.store8",
|
||||||
|
"i32.store16",
|
||||||
|
"i64.store8",
|
||||||
|
"i64.store16",
|
||||||
|
"i64.store32",
|
||||||
|
];
|
||||||
|
const MEMORY_INSTRS: InstrValue[] = [
|
||||||
|
...MEMORY_INSTRS_WITH_MEMARG.map((kind) => ins(kind, "memarg")),
|
||||||
|
ins("memory.size", []),
|
||||||
|
ins("memory.grow", []),
|
||||||
|
ins("memory.fill", []),
|
||||||
|
ins("memory.copy", []),
|
||||||
|
ins("memory.init", ["i32"]),
|
||||||
|
ins("data.drop", ["i32"]),
|
||||||
|
];
|
||||||
|
|
||||||
// . control
|
// . control
|
||||||
|
|
||||||
|
|
@ -278,6 +444,16 @@ export type Instr =
|
||||||
| MemoryInstr
|
| MemoryInstr
|
||||||
| ControlInstr;
|
| ControlInstr;
|
||||||
|
|
||||||
|
export const INSTRS: InstrValue[] = [
|
||||||
|
...NUMERIC_INSTRS,
|
||||||
|
...VECTOR_INSTRS,
|
||||||
|
...REFERENCE_INSTRS,
|
||||||
|
...PARAMETRIC_INSTRS,
|
||||||
|
...VARIABLE_INSTR,
|
||||||
|
...TABLE_INSTRS,
|
||||||
|
...MEMORY_INSTRS,
|
||||||
|
];
|
||||||
|
|
||||||
export type Expr = Instr[];
|
export type Expr = Instr[];
|
||||||
|
|
||||||
// Modules
|
// Modules
|
||||||
|
|
|
||||||
10
ui-tests/asm/drop.nil
Normal file
10
ui-tests/asm/drop.nil
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
//@check-pass
|
||||||
|
|
||||||
|
function dropping(a: I32) =
|
||||||
|
___asm(
|
||||||
|
__locals(),
|
||||||
|
"local.get 0",
|
||||||
|
"drop",
|
||||||
|
);
|
||||||
|
|
||||||
|
function main() = ;
|
||||||
7
ui-tests/asm/instr_not_string.nil
Normal file
7
ui-tests/asm/instr_not_string.nil
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
function a(a: I32) =
|
||||||
|
___asm(
|
||||||
|
__locals(),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
function main() = ;
|
||||||
4
ui-tests/asm/instr_not_string.stderr
Normal file
4
ui-tests/asm/instr_not_string.stderr
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
error: inline assembly instruction must be string literal with instruction
|
||||||
|
--> $DIR/instr_not_string.nil:4
|
||||||
|
4 | 0,
|
||||||
|
^
|
||||||
9
ui-tests/asm/invalid_instr.nil
Normal file
9
ui-tests/asm/invalid_instr.nil
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
//@check-pass
|
||||||
|
|
||||||
|
function dropping(a: I32) =
|
||||||
|
___asm(
|
||||||
|
__locals(),
|
||||||
|
"meow meow",
|
||||||
|
);
|
||||||
|
|
||||||
|
function main() = ;
|
||||||
4
ui-tests/asm/invalid_instr.stderr
Normal file
4
ui-tests/asm/invalid_instr.stderr
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
error: unknown instruction: meow
|
||||||
|
--> $DIR/invalid_instr.nil:6
|
||||||
|
6 | "meow meow",
|
||||||
|
^^^^^^^^^^^
|
||||||
9
ui-tests/asm/missing_locals.nil
Normal file
9
ui-tests/asm/missing_locals.nil
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
//@check-pass
|
||||||
|
|
||||||
|
function dropping(a: I32) =
|
||||||
|
___asm(
|
||||||
|
"local.get 0",
|
||||||
|
"drop",
|
||||||
|
);
|
||||||
|
|
||||||
|
function main() = ;
|
||||||
4
ui-tests/asm/missing_locals.stderr
Normal file
4
ui-tests/asm/missing_locals.stderr
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
error: inline assembly must have __locals() as first argument
|
||||||
|
--> $DIR/missing_locals.nil:4
|
||||||
|
4 | ___asm(
|
||||||
|
^
|
||||||
6
ui-tests/asm/not_toplevel.nil
Normal file
6
ui-tests/asm/not_toplevel.nil
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
function dropping(a: I32) = (
|
||||||
|
1;
|
||||||
|
___asm(__locals(), "drop");
|
||||||
|
);
|
||||||
|
|
||||||
|
function main() = ;
|
||||||
16
ui-tests/asm/not_toplevel.stderr
Normal file
16
ui-tests/asm/not_toplevel.stderr
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
error: `___asm` cannot be used as a value
|
||||||
|
--> $DIR/not_toplevel.nil:3
|
||||||
|
3 | ___asm(__locals(), "drop");
|
||||||
|
^^^^^^
|
||||||
|
error: `__locals` cannot be used as a value
|
||||||
|
--> $DIR/not_toplevel.nil:3
|
||||||
|
3 | ___asm(__locals(), "drop");
|
||||||
|
^^^^^^^^
|
||||||
|
error: expression of type <ERROR> is not callable
|
||||||
|
--> $DIR/not_toplevel.nil:3
|
||||||
|
3 | ___asm(__locals(), "drop");
|
||||||
|
^^^^^^^^
|
||||||
|
error: expression of type <ERROR> is not callable
|
||||||
|
--> $DIR/not_toplevel.nil:3
|
||||||
|
3 | ___asm(__locals(), "drop");
|
||||||
|
^^^^^^
|
||||||
17
ui-tests/asm/wrong_imm.nil
Normal file
17
ui-tests/asm/wrong_imm.nil
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
//@check-pass
|
||||||
|
|
||||||
|
function a(a: I32) =
|
||||||
|
___asm(
|
||||||
|
__locals(),
|
||||||
|
"local.get 0 0",
|
||||||
|
"drop",
|
||||||
|
);
|
||||||
|
|
||||||
|
function b(a: I32) =
|
||||||
|
___asm(
|
||||||
|
__locals(),
|
||||||
|
"local.get",
|
||||||
|
"drop",
|
||||||
|
);
|
||||||
|
|
||||||
|
function main() = ;
|
||||||
8
ui-tests/asm/wrong_imm.stderr
Normal file
8
ui-tests/asm/wrong_imm.stderr
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
error: mismatched immediate lengths, expected 1, got 2
|
||||||
|
--> $DIR/wrong_imm.nil:6
|
||||||
|
6 | "local.get 0 0",
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
error: mismatched immediate lengths, expected 1, got 0
|
||||||
|
--> $DIR/wrong_imm.nil:13
|
||||||
|
13 | "local.get",
|
||||||
|
^^^^^^^^^^^
|
||||||
17
ui-tests/type/generics/generics_structs_in_args.stderr
Normal file
17
ui-tests/type/generics/generics_structs_in_args.stderr
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
/home/nils/projects/riverdelta/target/ast.js:110
|
||||||
|
throw new Error(`substitution out of range, param index ${ty.idx} of param ${ty.name} out of range for length ${genericArgs.length}`);
|
||||||
|
^
|
||||||
|
|
||||||
|
Error: substitution out of range, param index 0 of param T out of range for length 0
|
||||||
|
at substituteTy (/home/nils/projects/riverdelta/target/ast.js:110:23)
|
||||||
|
at subst (/home/nils/projects/riverdelta/target/ast.js:106:27)
|
||||||
|
at Array.map (<anonymous>)
|
||||||
|
at substituteTy (/home/nils/projects/riverdelta/target/ast.js:125:45)
|
||||||
|
at typeOfItem (/home/nils/projects/riverdelta/target/typeck/item.js:193:33)
|
||||||
|
at Object.itemInner (/home/nils/projects/riverdelta/target/typeck/index.js:63:54)
|
||||||
|
at Object.item (/home/nils/projects/riverdelta/target/ast.js:146:34)
|
||||||
|
at /home/nils/projects/riverdelta/target/ast.js:164:55
|
||||||
|
at Array.map (<anonymous>)
|
||||||
|
at foldAst (/home/nils/projects/riverdelta/target/ast.js:164:34)
|
||||||
|
|
||||||
|
Node.js v18.18.2
|
||||||
Loading…
Add table
Add a link
Reference in a new issue