Skip to content

Move bswap to builtins. Remove bswap16 #2417

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 13 commits into from
Aug 15, 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
183 changes: 183 additions & 0 deletions src/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ export namespace BuiltinNames {
export const isManaged = "~lib/builtins/isManaged";
export const isVoid = "~lib/builtins/isVoid";

export const bswap = "~lib/builtins/bswap";

export const add = "~lib/builtins/add";
export const sub = "~lib/builtins/sub";
export const mul = "~lib/builtins/mul";
Expand Down Expand Up @@ -1144,6 +1146,187 @@ function builtin_idof(ctx: BuiltinContext): ExpressionRef {
}
builtins.set(BuiltinNames.idof, builtin_idof);

// bswap<T?>(value: T) -> T
function builtin_bswap(ctx: BuiltinContext): ExpressionRef {
var compiler = ctx.compiler;
var module = compiler.module;
if (
checkTypeOptional(ctx, true) |
checkArgsRequired(ctx, 1)
) return module.unreachable();

var typeArguments = ctx.typeArguments;
var arg0 = typeArguments
? compiler.compileExpression(
ctx.operands[0],
typeArguments[0].toUnsigned(),
Constraints.CONV_IMPLICIT | Constraints.MUST_WRAP
)
: compiler.compileExpression(
ctx.operands[0],
Type.u32,
Constraints.MUST_WRAP
);

var type = compiler.currentType;
if (type.isValue) {
switch (type.kind) {
case TypeKind.BOOL:
case TypeKind.I8:
case TypeKind.U8: return arg0;
case TypeKind.I16:
case TypeKind.U16: {
// <T>(x << 8 | x >> 8)
let flow = compiler.currentFlow;
let temp = flow.getTempLocal(type);
flow.setLocalFlag(temp.index, LocalFlags.WRAPPED);

let res = module.binary(
BinaryOp.OrI32,
module.binary(
BinaryOp.ShlI32,
module.local_tee(temp.index, arg0, false),
module.i32(8)
),
module.binary(
BinaryOp.ShrU32,
module.local_get(temp.index, TypeRef.I32),
module.i32(8)
)
);
// avoid wrapping for u16 due to it's already done for input arg
if (type.kind == TypeKind.I16) {
res = compiler.ensureSmallIntegerWrap(res, Type.i16);
}
flow.freeTempLocal(temp);
return res;
}
case TypeKind.I32:
case TypeKind.U32:
case TypeKind.ISIZE:
case TypeKind.USIZE: {
if (type.size == 32) {
// rotl(x & 0xFF00FF00, 8) | rotr(x & 0x00FF00FF, 8)
let flow = compiler.currentFlow;
let temp = flow.getTempLocal(type);
flow.setLocalFlag(temp.index, LocalFlags.WRAPPED);

let res = module.binary(
BinaryOp.OrI32,
module.binary(
BinaryOp.RotlI32,
module.binary(
BinaryOp.AndI32,
module.local_tee(temp.index, arg0, false),
module.i32(0xFF00FF00)
),
module.i32(8)
),
module.binary(
BinaryOp.RotrI32,
module.binary(
BinaryOp.AndI32,
module.local_get(temp.index, TypeRef.I32),
module.i32(0x00FF00FF)
),
module.i32(8)
),
);
flow.freeTempLocal(temp);
return res;
}
// fall-through
}
case TypeKind.I64:
case TypeKind.U64: {
// let t =
// ((x >>> 8) & 0x00FF00FF00FF00FF) |
// ((x & 0x00FF00FF00FF00FF) << 8)
//
// let res =
// ((t >>> 16) & 0x0000FFFF0000FFFF) |
// ((t & 0x0000FFFF0000FFFF) << 16)
//
// rotr(res, 32)

let flow = compiler.currentFlow;
let temp1 = flow.getTempLocal(type);
flow.setLocalFlag(temp1.index, LocalFlags.WRAPPED);
let temp2 = flow.getTempLocal(type);
flow.setLocalFlag(temp2.index, LocalFlags.WRAPPED);

// t = ((x >>> 8) & 0x00FF00FF00FF00FF) | ((x & 0x00FF00FF00FF00FF) << 8)
let expr = module.local_tee(
temp2.index,
module.binary(
BinaryOp.OrI64,
module.binary(
BinaryOp.AndI64,
module.binary(
BinaryOp.ShrU64,
module.local_tee(temp1.index, arg0, false),
module.i64(8)
),
module.i64(0x00FF00FF, 0x00FF00FF)
),
module.binary(
BinaryOp.ShlI64,
module.binary(
BinaryOp.AndI64,
module.local_get(temp1.index, TypeRef.I64),
module.i64(0x00FF00FF, 0x00FF00FF)
),
module.i64(8)
),
),
false
);

// ((t >>> 16) & 0x0000FFFF0000FFFF) | ((t & 0x0000FFFF0000FFFF) << 16)
let res = module.binary(
BinaryOp.OrI64,
module.binary(
BinaryOp.AndI64,
module.binary(
BinaryOp.ShrU64,
expr,
module.i64(16)
),
module.i64(0x0000FFFF, 0x0000FFFF)
),
module.binary(
BinaryOp.ShlI64,
module.binary(
BinaryOp.AndI64,
module.local_get(temp2.index, TypeRef.I64),
module.i64(0x0000FFFF, 0x0000FFFF)
),
module.i64(16)
),
);

// rotr(res, 32)
res = module.binary(
BinaryOp.RotrI64,
res,
module.i64(32)
);

flow.freeTempLocal(temp2);
flow.freeTempLocal(temp1);

return res;
}
}
}
compiler.error(
DiagnosticCode.Operation_0_cannot_be_applied_to_type_1,
ctx.reportNode.typeArgumentsRange, "bswap", type.toString()
);
return module.unreachable();
}
builtins.set(BuiltinNames.bswap, builtin_bswap);

// === Math ===================================================================================

// clz<T?>(value: T) -> T
Expand Down
12 changes: 12 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,18 @@ export class Type {
return nullableType;
}

/** Use unsigned type for according size if possible. */
toUnsigned(): Type {
switch (this.kind) {
case TypeKind.I8: return Type.u8;
case TypeKind.I16: return Type.u16;
case TypeKind.I32: return Type.u32;
case TypeKind.I64: return Type.u64;
case TypeKind.ISIZE: return this.size == 64 ? Type.usize64 : Type.usize32;
}
return this;
}

/** Tests if this type equals the specified. */
equals(other: Type): bool {
if (this.kind != other.kind) return false;
Expand Down
4 changes: 4 additions & 0 deletions std/assembly/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ export declare function isVoid<T>(): bool;
@builtin
export declare function lengthof<T>(func?: T): i32;

// @ts-ignore
@builtin
export declare function bswap<T>(value: T): T;

// @ts-ignore: decorator
@builtin
export declare function clz<T>(value: T): T;
Expand Down
9 changes: 2 additions & 7 deletions std/assembly/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ declare const ASC_VERSION_PATCH: i32;

// Builtins

/** Performs the sign-agnostic reverse bytes **/
declare function bswap<T extends i8 | u8 | i16 | u16 | i32 | u32 | i64 | u64 | isize | usize>(value: T): T;
/** Performs the sign-agnostic count leading zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered leading if the value is zero. */
declare function clz<T extends i32 | i64>(value: T): T;
/** Performs the sign-agnostic count tailing zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered trailing if the value is zero. */
Expand Down Expand Up @@ -1470,13 +1472,6 @@ declare function WARNING(message?: any): void;
/** Emits a user-defined diagnostic info when encountered. */
declare function INFO(message?: any): void;

// Polyfills

/** Performs the sign-agnostic reverse bytes **/
declare function bswap<T extends i8 | u8 | i16 | u16 | i32 | u32 | i64 | u64 | isize | usize>(value: T): T;
/** Performs the sign-agnostic reverse bytes only for last 16-bit **/
declare function bswap16<T extends i8 | u8 | i16 | u16 | i32 | u32>(value: T): T;

// Standard library

/** Memory operations. */
Expand Down
46 changes: 0 additions & 46 deletions std/assembly/polyfills.ts

This file was deleted.

9 changes: 2 additions & 7 deletions std/portable/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ declare const ASC_FEATURE_SIGN_EXTENSION: bool;

// Builtins

/** Performs the sign-agnostic reverse bytes **/
declare function bswap<T = i32 | u32 | isize | usize>(value: T): T;
/** Performs the sign-agnostic count leading zero bits operation on a 32-bit integer. All zero bits are considered leading if the value is zero. */
declare function clz<T = i32>(value: T): T;
/** Performs the sign-agnostic count tailing zero bits operation on a 32-bit integer. All zero bits are considered trailing if the value is zero. */
Expand Down Expand Up @@ -295,13 +297,6 @@ declare namespace f64 {
export function parseInt(string: string, radix?: i32): f64;
}

// Polyfills

/** [Polyfill] Performs the sign-agnostic reverse bytes **/
declare function bswap<T = i32 | u32 | isize | usize>(value: T): T;
/** [Polyfill] Performs the sign-agnostic reverse bytes only for last 16-bit **/
declare function bswap16<T = i16 | u16 | i32 | u32>(value: T): T;

// Standard library

declare const Mathf: typeof Math;
Expand Down
4 changes: 0 additions & 4 deletions std/portable/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,6 @@ if (typeof globalScope.ASC_TARGET === "undefined") {
return a | b;
};

globalScope["bswap16"] = function bswap16(value) {
return ((value << 8) & 0xFF00) | ((value >> 8) & 0x00FF) | (value & 0xFFFF0000);
};

function UnreachableError() {
if (Error.captureStackTrace) {
Error.captureStackTrace(this, UnreachableError);
Expand Down
Loading