Skip to content

Commit e7405b9

Browse files
committed
Make '%' operator resemble (strange) JS semantics
1 parent 7bd0326 commit e7405b9

File tree

6 files changed

+6772
-69
lines changed

6 files changed

+6772
-69
lines changed

src/compiler.ts

Lines changed: 44 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -3271,74 +3271,50 @@ export class Compiler extends DiagnosticEmitter {
32713271
expr = module.createBinary(BinaryOp.RemU64, leftExpr, rightExpr);
32723272
break;
32733273
}
3274-
3275-
// FIXME: JS floating-point '%' is neither a proper modulo nor remainder
3276-
// for now, there is f64/f32.mod/rem with C semantics
3277-
3278-
// case TypeKind.F32: {
3279-
// let remInstance = this.f32RemInstance;
3280-
// if (!remInstance) {
3281-
// let remNamespace = this.program.elementsLookup.get("f32");
3282-
// if (!remNamespace) {
3283-
// this.error(
3284-
// DiagnosticCode.Cannot_find_name_0,
3285-
// expression.range, "f32"
3286-
// );
3287-
// expr = module.createUnreachable();
3288-
// break;
3289-
// }
3290-
// let remPrototype = remNamespace.members ? remNamespace.members.get("rem") : null;
3291-
// if (!remPrototype) {
3292-
// this.error(
3293-
// DiagnosticCode.Cannot_find_name_0,
3294-
// expression.range, "f32.rem"
3295-
// );
3296-
// expr = module.createUnreachable();
3297-
// break;
3298-
// }
3299-
// assert(remPrototype.kind == ElementKind.FUNCTION_PROTOTYPE);
3300-
// this.f32RemInstance = remInstance = (<FunctionPrototype>remPrototype).resolve();
3301-
// }
3302-
// if (!(remInstance && this.compileFunction(remInstance))) {
3303-
// expr = module.createUnreachable();
3304-
// } else {
3305-
// expr = this.makeCallDirect(remInstance, [ leftExpr, rightExpr ]);
3306-
// }
3307-
// break;
3308-
// }
3309-
3310-
// case TypeKind.F64: {
3311-
// let remInstance = this.f64RemInstance;
3312-
// if (!remInstance) {
3313-
// let remNamespace = this.program.elementsLookup.get("f64");
3314-
// if (!remNamespace) {
3315-
// this.error(
3316-
// DiagnosticCode.Cannot_find_name_0,
3317-
// expression.range, "f64"
3318-
// );
3319-
// expr = module.createUnreachable();
3320-
// break;
3321-
// }
3322-
// let remPrototype = remNamespace.members ? remNamespace.members.get("rem") : null;
3323-
// if (!remPrototype) {
3324-
// this.error(
3325-
// DiagnosticCode.Cannot_find_name_0,
3326-
// expression.range, "f64.rem"
3327-
// );
3328-
// expr = module.createUnreachable();
3329-
// break;
3330-
// }
3331-
// assert(remPrototype.kind == ElementKind.FUNCTION_PROTOTYPE);
3332-
// this.f64RemInstance = remInstance = (<FunctionPrototype>remPrototype).resolve();
3333-
// }
3334-
// if (!(remInstance && this.compileFunction(remInstance))) {
3335-
// expr = module.createUnreachable();
3336-
// } else {
3337-
// expr = this.makeCallDirect(remInstance, [ leftExpr, rightExpr ]);
3338-
// }
3339-
// break;
3340-
// }
3341-
3274+
case TypeKind.F32: {
3275+
let remInstance = this.f32RemInstance;
3276+
if (!remInstance) {
3277+
let remPrototype = this.program.elementsLookup.get("jsremf");
3278+
if (!remPrototype) {
3279+
this.error(
3280+
DiagnosticCode.Cannot_find_name_0,
3281+
expression.range, "jsremf"
3282+
);
3283+
expr = module.createUnreachable();
3284+
break;
3285+
}
3286+
assert(remPrototype.kind == ElementKind.FUNCTION_PROTOTYPE);
3287+
this.f32RemInstance = remInstance = (<FunctionPrototype>remPrototype).resolve();
3288+
}
3289+
if (!(remInstance && this.compileFunction(remInstance))) {
3290+
expr = module.createUnreachable();
3291+
} else {
3292+
expr = this.makeCallDirect(remInstance, [ leftExpr, rightExpr ]);
3293+
}
3294+
break;
3295+
}
3296+
case TypeKind.F64: {
3297+
let remInstance = this.f64RemInstance;
3298+
if (!remInstance) {
3299+
let remPrototype = this.program.elementsLookup.get("jsrem");
3300+
if (!remPrototype) {
3301+
this.error(
3302+
DiagnosticCode.Cannot_find_name_0,
3303+
expression.range, "jsrem"
3304+
);
3305+
expr = module.createUnreachable();
3306+
break;
3307+
}
3308+
assert(remPrototype.kind == ElementKind.FUNCTION_PROTOTYPE);
3309+
this.f64RemInstance = remInstance = (<FunctionPrototype>remPrototype).resolve();
3310+
}
3311+
if (!(remInstance && this.compileFunction(remInstance))) {
3312+
expr = module.createUnreachable();
3313+
} else {
3314+
expr = this.makeCallDirect(remInstance, [ leftExpr, rightExpr ]);
3315+
}
3316+
break;
3317+
}
33423318
default: {
33433319
assert(false);
33443320
this.error(

std/assembly/builtins.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,13 @@ export namespace f64 {
172172
export declare const HEAP_BASE: usize;
173173

174174
export declare function start(): void;
175+
176+
/** @internal */
177+
export function jsrem(x: f64, y: f64): f64 {
178+
return copysign<f64>(NativeMath.mod(abs<f64>(x), abs<f64>(y)), x);
179+
}
180+
181+
/** @internal */
182+
export function jsremf(x: f32, y: f32): f32 {
183+
return copysign<f32>(NativeMathf.mod(abs<f32>(x), abs<f32>(y)), x);
184+
}

tests/compiler.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ tests.forEach(filename => {
118118
externalFunction: function() { },
119119
externalConstant: 1,
120120
logi: function(i) { console.log("logi: " + i); },
121-
logf: function(f) { console.log("logf: " + f); }
121+
logf: function(f) { console.log("logf: " + f); },
122+
jsrem: function(a, b) { return a % b; }
122123
},
123124
my: {
124125
externalFunction: function() { },

0 commit comments

Comments
 (0)