Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions lib/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -679,12 +679,17 @@ export default class Context {
const invalid = Boolean(mod.validate());
if (invalid) {
console.error('invalid:', invalid);
console.log(src);
// console.log(src);
return;
}
} catch (e) {
console.log('validate error:', src);
throw e;
} catch (e: any) {
console.log('Generated WAT did not pass validator:', e);

const lineNum = Number(e.message.split('\n')[1].split(':')[1]) - 1460;
this.module.src.debug(lineNum - 10, lineNum);

// throw e;
process.exit();
}
}

Expand Down
37 changes: 21 additions & 16 deletions lib/expr/branch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import ModuleManager from '../module.js';
import { DataExpr, Expr } from './expr.js';
import type { FunExpr, FunLocalTracker } from './fun.js';
import { DependentLocalExpr } from './util.js';
import wat, { WatCode } from '../wat.js';

/**
* Describes expensive expressions which were on the stack before a branch was invoked
Expand Down Expand Up @@ -33,22 +34,22 @@ export class BranchInputExpr extends DataExpr {
* @param fun
* @returns WAT
*/
capture(ctx: ModuleManager, fun: FunExpr) {
capture(ctx: ModuleManager, fun: FunExpr): WatCode {
// if (this.index)
// return '';
this.index = fun.addLocal(this.value.datatype);
return `${this.value.out(ctx, fun)}\n${fun.setLocalWat(this.index)}`;
return wat(this)`${this.value.out(ctx, fun)}\n${fun.setLocalWat(this.index)}`;
}

/**
* @override
*/
out(ctx: ModuleManager, fun: FunExpr) {
out(ctx: ModuleManager, fun: FunExpr): WatCode {
if (!this._datatype.isUnit() && !this.index) {
console.log('branch input: ', this.value);
console.log(new Error('bt'));
}
return fun.getLocalWat(this.index);
return wat(this)`${fun.getLocalWat(this.index)}`;
}

/**
Expand Down Expand Up @@ -123,20 +124,23 @@ export class BranchExpr extends Expr {
/**
* @override
*/
out(ctx: ModuleManager, fun: FunExpr) {
out(ctx: ModuleManager, fun: FunExpr): WatCode {
// Prevent multiple compilations
this._isCompiled = true;
const inputs = this.inputExprs.map(e => e.capture(ctx, fun)).join('\n\t');
// const inputs = this.inputExprs.map(e => e.capture(ctx, fun)).join('\n\t');
const inputs = new WatCode().concat(
...this.inputExprs.map(e => e.capture(ctx, fun)));

// Compile body
// Notice order of compilation from top to bottom so that locals are assigned before use
// TODO FIXME still not perfect... may need to convert tee-exprs
const conds = new Array(this.conditions.length);
const acts = new Array(this.actions.length);
const conds = new Array<WatCode>(this.conditions.length);
const acts = new Array<WatCode>(this.actions.length);
for (let i = this.conditions.length - 1; i >= 0; i--) {
const invIdx = (this.conditions.length - i) - 1;
conds[invIdx] = this.conditions[i].out(ctx, fun);
acts[invIdx] = this.actions[i].reverse().map(a => a.out(ctx, fun)).join(' ');
acts[invIdx] = new WatCode()
.concat(...this.actions[i].reverse().map(a => a.out(ctx, fun)));
}
// const conds = this.conditions.map(c => c.out(ctx, fun)).reverse();
// const acts = this.actions.map(a => a.map(v => v.out(ctx, fun)).join(' ')).reverse();
Expand All @@ -148,8 +152,8 @@ export class BranchExpr extends Expr {
this.results.forEach(r => r.setInds(fun));

// Last condition must be else clause
if (conds[conds.length - 1] != '(i32.const 1)') {
// console.log(conds[conds.length - 1]);
if (conds[conds.length - 1].toString() != '(i32.const 1)') {
console.log(conds[conds.length - 1].toString());
throw new error.SyntaxError("no else case for fun branch", this.tokens);
}

Expand Down Expand Up @@ -180,19 +184,20 @@ export class BranchExpr extends Expr {
// Compile to (if (...) (result ...) (then ...) (else ...))
// Note that there's some BS done here to work around multi-return if statements not being allowed :(
const retSet = this.results.map(r => fun.setLocalWat(r.getInds())).join('');
let ret: string = inputs + (function compileIf(i): string {
const self = this as Expr;
let ret: WatCode = inputs.concat((function compileIf(i): WatCode {
return i + 1 >= acts.length
? acts[i] + retSet
: `${conds[i]}\n\t(if ${
? wat(self)`${acts[i]}${retSet}`
: wat(self as any)`${conds[i]}\n\t(if ${
retType.length === 1 ? `(result ${retType})` : ''
}\n\t(then ${
acts[i] + retSet
})\n\t(else ${
compileIf(i + 1)
}))`;
})(0);
})(0));
if (retType.length === 1)
ret += '\n\t' + retSet;
ret.add(this, '\n\t' + retSet);

// console.log('BranchExpr', ret);
return ret;
Expand Down
16 changes: 9 additions & 7 deletions lib/expr/closure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { InternalFunExpr, FunExpr, ParamExpr } from './fun.js';
import { LiteralMacro, Macro } from '../macro.js';
import { DependentLocalExpr, ProxyExpr, TeeExpr } from './util.js';
import Context from '../context.js';
import wat, { WatCode } from '../wat.js';

/**
* Capture lexically scoped variables and store them into a new closure object
Expand Down Expand Up @@ -96,7 +97,7 @@ export class ClosureCreateExpr extends DataExpr {
this.func.nparams = 1 + this.params.length;
}

out(ctx: ModuleManager, fun: FunExpr) {
out(ctx: ModuleManager, fun: FunExpr): WatCode {
// Add function to module table once only
if (!this._isCompiled) {
ctx.addFunction(this.func);
Expand All @@ -107,7 +108,7 @@ export class ClosureCreateExpr extends DataExpr {
// this is super painful ... maybe check to see if they've been compiled yet?
// maybe extend expressions?

return '';
return new WatCode();
}
children(): Expr[] {
throw new Error('todo');
Expand All @@ -131,18 +132,19 @@ export class ClosureInvokeExpr extends Expr {
new DependentLocalExpr(token, t as types.DataType, this));
}

out(ctx: ModuleManager, fun: FunExpr) {
out(ctx: ModuleManager, fun: FunExpr): WatCode {
// DependentLocalExpr checks this
this._isCompiled = true;

// Load function index from closure object pointer on the stack
// And invoke function via function table
// The first argument being the closure object
this.outputs.forEach(r => r.setInds(fun));
let ret = this.closure.out(ctx, fun);
ret += '(i32.load (global.get $__ref_sp)) (call_indirect)';
ret += this.outputs.map(r => fun.setLocalWat(r.getInds()));
return ret;
return wat(this)`${
this.closure.out(ctx, fun)
}(i32.load (global.get $__ref_sp)) (call_indirect)${
this.outputs.map(r => fun.setLocalWat(r.getInds())).join('')
}`;
}

invoke(ctx: Context) {
Expand Down
50 changes: 26 additions & 24 deletions lib/expr/enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import type { LexerToken } from '../scan.js';
import type ModuleManager from '../module.js';
import { Expr, DataExpr } from './expr.js';
import type { FunExpr } from './fun.js';
import { constructGc, loadRef } from './gc_util.js';
import { DependentLocalExpr, fromDataValue } from './util.js';
import { BranchInputExpr } from './branch.js';
import { uid } from '../util.js';
import Context from '../context.js';
import wat, { WatCode } from '../wat.js';

// Check if an enum contains specified value
// export class EnumContainsCheckExpr extends DataExpr {
Expand Down Expand Up @@ -37,7 +37,7 @@ import Context from '../context.js';
// // Need to determine dynamically
// if (eedt instanceof types.EnumBaseType)
// return `${this.enumExpr.out(ctx, fun)
// }\n\t(call $__ref_stack_pop)(i32.load)(i32.const ${
// } (call $__ref_stack_pop)(i32.load)(i32.const ${
// this.checkType.index})(i32.eq)`;

// throw new SyntaxError('Not cannot check if non-enum contains type', [this.enumExpr.token, this.token]);
Expand All @@ -57,17 +57,19 @@ export class EnumGetExpr extends DataExpr {
super(token, dt.type);
}

out(ctx: ModuleManager, fun?: FunExpr) {
out(ctx: ModuleManager, fun?: FunExpr): WatCode {
if (!this.results)
return this.enumExpr.out(ctx, fun)
+ '(drop)'
+ loadRef(new types.RefType(this.token, this._datatype), fun);
return wat(this)`${
this.enumExpr.out(ctx, fun)
}(drop)${
this.loadRef(new types.RefType(this.token, this._datatype), fun)
}`;

this.results.forEach(r => r.inds = fun.addLocal(r.datatype));
return `${
return wat(this)`${
this.enumExpr.out(ctx, fun)
} (drop) ${
loadRef(new types.RefType(this.token, this._datatype), fun)
this.loadRef(new types.RefType(this.token, this._datatype), fun)
} ${
this.results.map(r => fun.setLocalWat(r.inds)).join(' ')
}`;
Expand Down Expand Up @@ -113,7 +115,7 @@ export class EnumTypeIndexExpr extends DataExpr {
return this.enumExpr.children();
}

out(ctx: ModuleManager, fun?: FunExpr) {
out(ctx: ModuleManager, fun?: FunExpr): WatCode {
// let eedt = this.enumExpr.datatype;
// if (eedt instanceof types.ClassType)
// eedt = eedt.getBaseType();
Expand All @@ -123,9 +125,9 @@ export class EnumTypeIndexExpr extends DataExpr {


// Simply discard the reference
return this.enumExpr.out(ctx, fun)
return wat(this)`${this.enumExpr.out(ctx, fun)
}(global.set $__ref_sp (i32.add (global.get $__ref_sp) (i32.const 4)))`;
// equiv (call $__ref_stack_pop) (drop)
+ '(global.set $__ref_sp (i32.add (global.get $__ref_sp) (i32.const 4)))'
}
}

Expand All @@ -144,13 +146,13 @@ export class EnumConstructor extends DataExpr {
super(token, enumClassType);
}

out(ctx: ModuleManager, fun?: FunExpr): string {
out(ctx: ModuleManager, fun?: FunExpr): WatCode {
const v = this.knownValue instanceof value.Value
? fromDataValue([this.knownValue])
: this.knownValue;
return `(i32.const ${this._datatype.index})\n\t${
v.map(v => v.out(ctx, fun)).join(' ')
}\n\t${constructGc(this.enumClassType.type, ctx, fun)}`;
return wat(this)`(i32.const ${String(this._datatype.index)}) ${
v.map(v => v.out(ctx, fun))
} ${this.constructGc(this.enumClassType.type, ctx, fun)}`;
}

children(): Expr[] {
Expand Down Expand Up @@ -189,27 +191,27 @@ export class EnumMatchExpr extends Expr {
// .concat(...this.results.map(r => r.children()));
}

out(ctx: ModuleManager, fun: FunExpr): string {
out(ctx: ModuleManager, fun: FunExpr): WatCode {
// Prevent multiple compilations
this._isCompiled = true;

let ret = this.inputs.map(inp => inp.capture(ctx, fun)).join('\n\t');
const inpWat = this.inputs.map(inp => inp.capture(ctx, fun))
this.results.forEach(r => r.inds = fun.addLocal(r.datatype));

const retType = this.outputTypes.map(t => t.getWasmTypeName()).join(' ');
const branchId = `$branch_${uid()}`;
ret += `(block ${branchId} (result ${retType}) ${
const ret = wat(this)`${inpWat}(block ${branchId} (result ${retType}) ${
this.branches.slice(1).map(() => '(block ').join('')
} (block (block ${
this.typeIndexExpr.out(ctx, fun)
}\n\t (br_table ${
} (br_table ${
this.branchBindings.map(n => String(n + 1)).join(' ')
} 0)) unreachable) ${
this.branches
.map(vs => vs.map(v => v.out(ctx, fun)).join(' '))
.join(`(br ${branchId}) )`)
} )`;
ret += this.results.map(dl => fun.setLocalWat(dl.inds));
wat.join(this, `(br ${branchId}) )`,
...this.branches.map(vs => vs.map(v => v.out(ctx, fun))))
} ) ${this.results.map(dl => fun.setLocalWat(dl.inds)).join(' ')}`;

console.log('this should error if not 1 or 0: ', this.results.length)

return ret;
}
Expand Down
Loading