Skip to content

Add --uncheckedBehavior to customize the use of unchecked() contexts #2575

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 22, 2022
Merged
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
9 changes: 8 additions & 1 deletion cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,14 +295,20 @@ export async function main(argv, options) {
}

// Set up options
let program, runtime;
let program, runtime, uncheckedBehavior;
const compilerOptions = assemblyscript.newOptions();
switch (opts.runtime) {
case "stub": runtime = 0; break;
case "minimal": runtime = 1; break;
/* incremental */
default: runtime = 2; break;
}
switch (opts.uncheckedBehavior) {
/* default */
default: uncheckedBehavior = 0; break;
case "never": uncheckedBehavior = 1; break;
case "always": uncheckedBehavior = 2; break;
}
assemblyscript.setTarget(compilerOptions, 0);
assemblyscript.setDebugInfo(compilerOptions, !!opts.debug);
assemblyscript.setRuntime(compilerOptions, runtime);
Expand All @@ -320,6 +326,7 @@ export async function main(argv, options) {
assemblyscript.setMemoryBase(compilerOptions, opts.memoryBase >>> 0);
assemblyscript.setTableBase(compilerOptions, opts.tableBase >>> 0);
assemblyscript.setSourceMap(compilerOptions, opts.sourceMap != null);
assemblyscript.setUncheckedBehavior(compilerOptions, uncheckedBehavior);
assemblyscript.setNoUnsafe(compilerOptions, opts.noUnsafe);
assemblyscript.setPedantic(compilerOptions, opts.pedantic);
assemblyscript.setLowMemoryLimit(compilerOptions, opts.lowMemoryLimit >>> 0);
Expand Down
16 changes: 16 additions & 0 deletions cli/options.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,22 @@
],
"type": "s"
},
"uncheckedBehavior": {
"category": "Debugging",
"description": [
"Changes the behavior of unchecked() expressions.",
"Using this option can potentially cause breakage.",
"",
" default The default behavior: unchecked operations are",
" only used inside of unchecked().",
" never Unchecked operations are never used, even when",
" inside of unchecked().",
" always Unchecked operations are always used if possible,",
" whether or not unchecked() is used."
],
"type": "s",
"default": "default"
},
"debug": {
"category": "Debugging",
"description": "Enables debug information in emitted binaries.",
Expand Down
7 changes: 5 additions & 2 deletions src/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
import {
Compiler,
Constraints,
RuntimeFeatures
RuntimeFeatures,
UncheckedBehavior
} from "./compiler";

import {
Expand Down Expand Up @@ -3587,8 +3588,10 @@ function builtin_unchecked(ctx: BuiltinContext): ExpressionRef {
checkArgsRequired(ctx, 1)
) return module.unreachable();
let flow = compiler.currentFlow;
let ignoreUnchecked = compiler.options.uncheckedBehavior === UncheckedBehavior.Never;
let alreadyUnchecked = flow.is(FlowFlags.UncheckedContext);
flow.set(FlowFlags.UncheckedContext);
if (ignoreUnchecked) assert(!alreadyUnchecked);
else flow.set(FlowFlags.UncheckedContext);
// eliminate unnecessary tees by preferring contextualType(=void)
let expr = compiler.compileExpression(ctx.operands[0], ctx.contextualType);
if (!alreadyUnchecked) flow.unset(FlowFlags.UncheckedContext);
Expand Down
12 changes: 12 additions & 0 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ export class Options {
exportTable: bool = false;
/** If true, generates information necessary for source maps. */
sourceMap: bool = false;
/** Unchecked behavior. Defaults to only using unchecked operations inside unchecked(). */
uncheckedBehavior: UncheckedBehavior = UncheckedBehavior.Default;
/** If given, exports the start function instead of calling it implicitly. */
exportStart: string | null = null;
/** Static memory start offset. */
Expand Down Expand Up @@ -315,6 +317,16 @@ export class Options {
}
}

/** Behaviors regarding unchecked operations. */
export const enum UncheckedBehavior {
/** Only use unchecked operations inside unchecked(). */
Default = 0,
/** Never use unchecked operations. */
Never = 1,
/** Always use unchecked operations if possible. */
Always = 2
}

/** Various constraints in expression compilation. */
export const enum Constraints {
None = 0,
Expand Down
10 changes: 10 additions & 0 deletions src/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ import {
CommonFlags
} from "./common";

import {
UncheckedBehavior
} from "./compiler";

import {
DiagnosticCode
} from "./diagnostics";
Expand Down Expand Up @@ -203,6 +207,9 @@ export class Flow {
if (targetFunction.is(CommonFlags.Constructor)) {
flow.initThisFieldFlags();
}
if (targetFunction.program.options.uncheckedBehavior === UncheckedBehavior.Always) {
flow.set(FlowFlags.UncheckedContext);
}
return flow;
}

Expand All @@ -215,6 +222,9 @@ export class Flow {
if (inlineFunction.is(CommonFlags.Constructor)) {
flow.initThisFieldFlags();
}
if (targetFunction.program.options.uncheckedBehavior === UncheckedBehavior.Always) {
flow.set(FlowFlags.UncheckedContext);
}
return flow;
}

Expand Down
8 changes: 7 additions & 1 deletion src/index-wasm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import {

import {
Compiler,
Options
Options,
UncheckedBehavior
} from "./compiler";

import {
Expand Down Expand Up @@ -102,6 +103,11 @@ export function setSourceMap(options: Options, sourceMap: bool): void {
options.sourceMap = sourceMap;
}

/** Sets the `uncheckedBehavior` option. */
export function setUncheckedBehavior(options: Options, uncheckedBehavior: UncheckedBehavior): void {
options.uncheckedBehavior = uncheckedBehavior;
}

/** Sets the `memoryBase` option. */
export function setMemoryBase(options: Options, memoryBase: u32): void {
options.memoryBase = memoryBase;
Expand Down