Skip to content

Add rem builtin for i32 and i64 (#1310) #2306

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 4 commits into from
Jun 10, 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
1 change: 1 addition & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ under the licensing terms detailed in LICENSE:
* Syed Jafri <[email protected]>
* Peter Hayman <[email protected]>
* ApsarasX <[email protected]>
* Adrien Zinger <[email protected]>

Portions of this software are derived from third-party works licensed under
the following terms:
Expand Down
98 changes: 97 additions & 1 deletion src/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export namespace BuiltinNames {
export const trunc = "~lib/builtins/trunc";
export const eq = "~lib/builtins/eq";
export const ne = "~lib/builtins/ne";
export const rem = "~lib/builtins/rem";
export const load = "~lib/builtins/load";
export const store = "~lib/builtins/store";
export const atomic_load = "~lib/builtins/atomic.load";
Expand Down Expand Up @@ -269,6 +270,11 @@ export namespace BuiltinNames {
export const f32_ne = "~lib/builtins/f32.ne";
export const f64_ne = "~lib/builtins/f64.ne";

export const i32_rem_s = "~lib/builtins/i32.rem_s";
export const i32_rem_u = "~lib/builtins/i32.rem_u";
export const i64_rem_s = "~lib/builtins/i64.rem_s";
export const i64_rem_u = "~lib/builtins/i64.rem_u";

export const i32_load8_s = "~lib/builtins/i32.load8_s";
export const i32_load8_u = "~lib/builtins/i32.load8_u";
export const i32_load16_s = "~lib/builtins/i32.load16_s";
Expand Down Expand Up @@ -815,7 +821,7 @@ function builtin_isString(ctx: BuiltinContext): ExpressionRef {
compiler.currentType = Type.bool;
if (!type) return module.unreachable();
var classReference = type.getClass();
return reifyConstantType(ctx,
return reifyConstantType(ctx,
module.i32(
classReference && classReference.isAssignableTo(compiler.program.stringInstance)
? 1
Expand Down Expand Up @@ -2231,6 +2237,60 @@ function builtin_store(ctx: BuiltinContext): ExpressionRef {
}
builtins.set(BuiltinNames.store, builtin_store);

// rem<T?>(left: T, right: T) -> T
function builtin_rem(ctx: BuiltinContext): ExpressionRef {
var compiler = ctx.compiler;
var module = compiler.module;
if (checkTypeOptional(ctx, true) | checkArgsRequired(ctx, 2)) {
return module.unreachable();
}
var operands = ctx.operands;
var typeArguments = ctx.typeArguments;
var left = operands[0];
var arg0 = typeArguments
? compiler.compileExpression(
left,
typeArguments[0],
Constraints.CONV_IMPLICIT
)
: compiler.compileExpression(operands[0], Type.auto);
var type = compiler.currentType;
if (type.isIntegerValue) {
let arg1: ExpressionRef;
if (!typeArguments && left.isNumericLiteral) {
// prefer right type
arg1 = compiler.compileExpression(
operands[1],
type
);
if (compiler.currentType != type) {
arg0 = compiler.compileExpression(
left,
(type = compiler.currentType),
Constraints.CONV_IMPLICIT
);
}
} else {
arg1 = compiler.compileExpression(
operands[1],
type,
Constraints.CONV_IMPLICIT
);
}
if (type.isIntegerValue) {
return compiler.makeRem(arg0, arg1, type, ctx.reportNode);
}
}
compiler.error(
DiagnosticCode.Operation_0_cannot_be_applied_to_type_1,
ctx.reportNode.typeArgumentsRange,
"rem",
type.toString()
);
return module.unreachable();
}
builtins.set(BuiltinNames.rem, builtin_rem);

// add<T?>(left: T, right: T) -> T
function builtin_add(ctx: BuiltinContext): ExpressionRef {
var compiler = ctx.compiler;
Expand Down Expand Up @@ -6638,6 +6698,42 @@ function builtin_f64_trunc(ctx: BuiltinContext): ExpressionRef {
}
builtins.set(BuiltinNames.f64_trunc, builtin_f64_trunc);

// i32.rem_s -> rem<i32>
function builtin_i32_rem_s(ctx: BuiltinContext): ExpressionRef {
checkTypeAbsent(ctx);
ctx.typeArguments = [ Type.i32 ];
ctx.contextualType = Type.i32;
return builtin_rem(ctx);
}
builtins.set(BuiltinNames.i32_rem_s, builtin_i32_rem_s);

// i32.rem_u -> rem<u32>
function builtin_i32_rem_u(ctx: BuiltinContext): ExpressionRef {
checkTypeAbsent(ctx);
ctx.typeArguments = [ Type.u32 ];
ctx.contextualType = Type.u32;
return builtin_rem(ctx);
}
builtins.set(BuiltinNames.i32_rem_u, builtin_i32_rem_u);

// i64.rem_s -> rem<i64>
function builtin_i64_rem_s(ctx: BuiltinContext): ExpressionRef {
checkTypeAbsent(ctx);
ctx.typeArguments = [ Type.i64 ];
ctx.contextualType = Type.i64;
return builtin_rem(ctx);
}
builtins.set(BuiltinNames.i64_rem_s, builtin_i64_rem_s);

// i64.rem_u -> rem<u64>
function builtin_i64_rem_u(ctx: BuiltinContext): ExpressionRef {
checkTypeAbsent(ctx);
ctx.typeArguments = [ Type.u64 ];
ctx.contextualType = Type.u64;
return builtin_rem(ctx);
}
builtins.set(BuiltinNames.i64_rem_u, builtin_i64_rem_u);

// i32.add -> add<i32>
function builtin_i32_add(ctx: BuiltinContext): ExpressionRef {
checkTypeAbsent(ctx);
Expand Down
20 changes: 20 additions & 0 deletions std/assembly/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ export declare function eq<T>(left: T, right: T): i32;
@builtin
export declare function ne<T>(left: T, right: T): i32;

// @ts-ignore: decorator
@builtin
export declare function rem<T>(left: T, right: T): T;

// @ts-ignore: decorator
@unsafe @builtin
export declare function load<T>(ptr: usize, immOffset?: usize, immAlign?: usize): T;
Expand Down Expand Up @@ -350,6 +354,14 @@ export namespace i32 {
@builtin
export declare function ne(left: i32, right:i32): i32;

// @ts-ignore: decorator
@builtin
export declare function rem_s(left: i32, right: i32): i32;

// @ts-ignore: decorator
@builtin
export declare function rem_u(left: u32, right: u32): u32;

// @ts-ignore: decorator
@builtin
export declare function reinterpret_f32(value: f32): i32;
Expand Down Expand Up @@ -601,6 +613,14 @@ export namespace i64 {
@builtin
export declare function ne(left: i64, right:i64): i32;

// @ts-ignore: decorator
@builtin
export declare function rem_s(left: i64, right: i64): i64;

// @ts-ignore: decorator
@builtin
export declare function rem_u(left: u64, right: u64): u64;

// @ts-ignore: decorator
@builtin
export declare function reinterpret_f64(value: f64): i64;
Expand Down
20 changes: 16 additions & 4 deletions std/assembly/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ declare function div<T extends i32 | i64 | f32 | f64>(left: T, right: T): T;
declare function eq<T extends i32 | i64 | f32 | f64>(left: T, right: T): i32;
/** Return 0 if two numbers are equal to each other, 1 otherwise. */
declare function ne<T extends i32 | i64 | f32 | f64>(left: T, right: T): i32;
/** Computes the remainder of two integers. */
declare function rem<T extends i32 | i64>(left: T, right: T): T;
/** Loads a value of the specified type from memory. Equivalent to dereferncing a pointer in other languages. */
declare function load<T>(ptr: usize, immOffset?: usize, immAlign?: usize): T;
/** Stores a value of the specified type to memory. Equivalent to dereferencing a pointer in other languages when assigning a value. */
Expand Down Expand Up @@ -342,10 +344,15 @@ declare namespace i32 {
export function div_s(left: i32, right: i32): i32;
/** Computes the unsigned quotient of two 32-bit integers. */
export function div_u(left: i32, right: i32): i32;
/** Return 1 two 32-bit inegers are equal to each other, 0 otherwise. */
/** Return 1 if two 32-bit integers are equal to each other, 0 otherwise. */
export function eq(left: i32, right: i32): i32;
/** Return 0 two 32-bit inegers are equal to each other, 1 otherwise. */
/** Return 0 if two 32-bit integers are equal to each other, 1 otherwise. */
export function ne(left: i32, right: i32): i32;
/** Computes the signed remainder of two 32-bit integers. */
export function rem_s(left: i32, right: i32): i32;
/** Computes the unsigned remainder of two 32-bit integers. */
export function rem_u(left: u32, right: u32): u32;

/** Atomic 32-bit integer operations. */
export namespace atomic {
/** Atomically loads an 8-bit unsigned integer value from memory and returns it as a 32-bit integer. */
Expand Down Expand Up @@ -466,10 +473,15 @@ declare namespace i64 {
export function div_s(left: i64, right: i64): i64;
/** Computes the unsigned quotient of two 64-bit integers. */
export function div_u(left: i64, right: i64): i64;
/** Return 1 two 64-bit inegers are equal to each other, 0 otherwise. */
/** Return 1 if two 64-bit integers are equal to each other, 0 otherwise. */
export function eq(left: i64, right: i64): i32;
/** Return 0 two 64-bit inegers are equal to each other, 1 otherwise. */
/** Return 0 if two 64-bit integers are equal to each other, 1 otherwise. */
export function ne(left: i64, right: i64): i32;
/** Computes the signed remainder of two 64-bit integers. */
export function rem_s(left: i64, right: i64): i64;
/** Computes the unsigned remainder of two 64-bit integers. */
export function rem_u(left: u64, right: u64): u64;

/** Atomic 64-bit integer operations. */
export namespace atomic {
/** Atomically loads an 8-bit unsigned integer value from memory and returns it as a 64-bit integer. */
Expand Down
Loading