Skip to content

Commit 7a35ff8

Browse files
authored
feat: Add --uncheckedBehavior to customize the use of unchecked() (#2575)
1 parent d3410f4 commit 7a35ff8

File tree

6 files changed

+58
-4
lines changed

6 files changed

+58
-4
lines changed

Diff for: cli/index.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -295,14 +295,20 @@ export async function main(argv, options) {
295295
}
296296

297297
// Set up options
298-
let program, runtime;
298+
let program, runtime, uncheckedBehavior;
299299
const compilerOptions = assemblyscript.newOptions();
300300
switch (opts.runtime) {
301301
case "stub": runtime = 0; break;
302302
case "minimal": runtime = 1; break;
303303
/* incremental */
304304
default: runtime = 2; break;
305305
}
306+
switch (opts.uncheckedBehavior) {
307+
/* default */
308+
default: uncheckedBehavior = 0; break;
309+
case "never": uncheckedBehavior = 1; break;
310+
case "always": uncheckedBehavior = 2; break;
311+
}
306312
assemblyscript.setTarget(compilerOptions, 0);
307313
assemblyscript.setDebugInfo(compilerOptions, !!opts.debug);
308314
assemblyscript.setRuntime(compilerOptions, runtime);
@@ -320,6 +326,7 @@ export async function main(argv, options) {
320326
assemblyscript.setMemoryBase(compilerOptions, opts.memoryBase >>> 0);
321327
assemblyscript.setTableBase(compilerOptions, opts.tableBase >>> 0);
322328
assemblyscript.setSourceMap(compilerOptions, opts.sourceMap != null);
329+
assemblyscript.setUncheckedBehavior(compilerOptions, uncheckedBehavior);
323330
assemblyscript.setNoUnsafe(compilerOptions, opts.noUnsafe);
324331
assemblyscript.setPedantic(compilerOptions, opts.pedantic);
325332
assemblyscript.setLowMemoryLimit(compilerOptions, opts.lowMemoryLimit >>> 0);

Diff for: cli/options.json

+16
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,22 @@
9898
],
9999
"type": "s"
100100
},
101+
"uncheckedBehavior": {
102+
"category": "Debugging",
103+
"description": [
104+
"Changes the behavior of unchecked() expressions.",
105+
"Using this option can potentially cause breakage.",
106+
"",
107+
" default The default behavior: unchecked operations are",
108+
" only used inside of unchecked().",
109+
" never Unchecked operations are never used, even when",
110+
" inside of unchecked().",
111+
" always Unchecked operations are always used if possible,",
112+
" whether or not unchecked() is used."
113+
],
114+
"type": "s",
115+
"default": "default"
116+
},
101117
"debug": {
102118
"category": "Debugging",
103119
"description": "Enables debug information in emitted binaries.",

Diff for: src/builtins.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
import {
2727
Compiler,
2828
Constraints,
29-
RuntimeFeatures
29+
RuntimeFeatures,
30+
UncheckedBehavior
3031
} from "./compiler";
3132

3233
import {
@@ -3587,8 +3588,10 @@ function builtin_unchecked(ctx: BuiltinContext): ExpressionRef {
35873588
checkArgsRequired(ctx, 1)
35883589
) return module.unreachable();
35893590
let flow = compiler.currentFlow;
3591+
let ignoreUnchecked = compiler.options.uncheckedBehavior === UncheckedBehavior.Never;
35903592
let alreadyUnchecked = flow.is(FlowFlags.UncheckedContext);
3591-
flow.set(FlowFlags.UncheckedContext);
3593+
if (ignoreUnchecked) assert(!alreadyUnchecked);
3594+
else flow.set(FlowFlags.UncheckedContext);
35923595
// eliminate unnecessary tees by preferring contextualType(=void)
35933596
let expr = compiler.compileExpression(ctx.operands[0], ctx.contextualType);
35943597
if (!alreadyUnchecked) flow.unset(FlowFlags.UncheckedContext);

Diff for: src/compiler.ts

+12
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ export class Options {
245245
exportTable: bool = false;
246246
/** If true, generates information necessary for source maps. */
247247
sourceMap: bool = false;
248+
/** Unchecked behavior. Defaults to only using unchecked operations inside unchecked(). */
249+
uncheckedBehavior: UncheckedBehavior = UncheckedBehavior.Default;
248250
/** If given, exports the start function instead of calling it implicitly. */
249251
exportStart: string | null = null;
250252
/** Static memory start offset. */
@@ -315,6 +317,16 @@ export class Options {
315317
}
316318
}
317319

320+
/** Behaviors regarding unchecked operations. */
321+
export const enum UncheckedBehavior {
322+
/** Only use unchecked operations inside unchecked(). */
323+
Default = 0,
324+
/** Never use unchecked operations. */
325+
Never = 1,
326+
/** Always use unchecked operations if possible. */
327+
Always = 2
328+
}
329+
318330
/** Various constraints in expression compilation. */
319331
export const enum Constraints {
320332
None = 0,

Diff for: src/flow.ts

+10
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ import {
7878
CommonFlags
7979
} from "./common";
8080

81+
import {
82+
UncheckedBehavior
83+
} from "./compiler";
84+
8185
import {
8286
DiagnosticCode
8387
} from "./diagnostics";
@@ -203,6 +207,9 @@ export class Flow {
203207
if (targetFunction.is(CommonFlags.Constructor)) {
204208
flow.initThisFieldFlags();
205209
}
210+
if (targetFunction.program.options.uncheckedBehavior === UncheckedBehavior.Always) {
211+
flow.set(FlowFlags.UncheckedContext);
212+
}
206213
return flow;
207214
}
208215

@@ -215,6 +222,9 @@ export class Flow {
215222
if (inlineFunction.is(CommonFlags.Constructor)) {
216223
flow.initThisFieldFlags();
217224
}
225+
if (targetFunction.program.options.uncheckedBehavior === UncheckedBehavior.Always) {
226+
flow.set(FlowFlags.UncheckedContext);
227+
}
218228
return flow;
219229
}
220230

Diff for: src/index-wasm.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import {
2222

2323
import {
2424
Compiler,
25-
Options
25+
Options,
26+
UncheckedBehavior
2627
} from "./compiler";
2728

2829
import {
@@ -102,6 +103,11 @@ export function setSourceMap(options: Options, sourceMap: bool): void {
102103
options.sourceMap = sourceMap;
103104
}
104105

106+
/** Sets the `uncheckedBehavior` option. */
107+
export function setUncheckedBehavior(options: Options, uncheckedBehavior: UncheckedBehavior): void {
108+
options.uncheckedBehavior = uncheckedBehavior;
109+
}
110+
105111
/** Sets the `memoryBase` option. */
106112
export function setMemoryBase(options: Options, memoryBase: u32): void {
107113
options.memoryBase = memoryBase;

0 commit comments

Comments
 (0)