use alloc::vec::Vec;
use bytecode_lang::{
Const, ImportId, Inst, IntTy, Kind, Label, Overflow, Policy, Prim as BPrim, Reg, Shift, ValType,
};
use hir_lang::{ExprId, NodeRef, Symbol};
use intern_lang::Lookup;
use super::{Dst, Gen, Step, Task, Val};
use crate::error::CompileError;
use crate::func::FnCx;
use crate::intrinsics::{Fast, Lowering, LsbOp};
#[derive(Clone, Copy, Debug)]
pub(crate) enum IntrinsicStep {
Lsb {
op: LsbOp,
n: usize,
dst: Dst,
node: NodeRef,
},
Host {
fast: Option<Fast>,
import: ImportId,
win: (u32, Reg),
argc: u8,
dst: Dst,
node: NodeRef,
},
}
impl<L: Lookup> Gen<'_, '_, L> {
pub(crate) fn intrinsic_expr(
&mut self,
e: ExprId,
name: Symbol,
generic_args: &[hir_lang::GenericArg],
args: &[ExprId],
dst: Dst,
) -> Result<(), CompileError> {
let node = NodeRef::Expr(e);
let text = self.cx.text(name);
let Some(def) = self.cx.opts.intrinsics().get(&text) else {
return Err(CompileError::UnknownIntrinsic {
name,
node,
span: self.cx.span(node),
});
};
if let Lowering::Class { op, module, name } = def.lowering() {
let ty = generic_args.iter().find_map(|g| match *g {
hir_lang::GenericArg::Ty(t) => Some(t),
_ => None,
});
let list: Vec<hir_lang::Arg> =
args.iter().map(|&a| hir_lang::Arg::positional(a)).collect();
let host = (module.clone(), name.clone());
let op = *op;
return self.class_op(op, host, ty, &list, dst, node);
}
if !def.arity().accepts(args.len()) {
return Err(CompileError::IntrinsicArity {
node,
span: self.cx.span(node),
arity: def.arity(),
found: u32::try_from(args.len()).unwrap_or(u32::MAX),
});
}
let (fast, module, func) = match def.lowering() {
Lowering::Lsb(op) => {
let op = *op;
self.tasks
.push(Task::Step(Step::Intrinsic(IntrinsicStep::Lsb {
op,
n: args.len(),
dst,
node,
})));
for i in (0..args.len()).rev() {
let d = self.operand_dst(args, i);
if let Some(&x) = args.get(i) {
self.tasks.push(Task::Expr(x, d));
}
}
return Ok(());
}
Lowering::Host { module, name } => (None, module.clone(), name.clone()),
Lowering::Fast { fast, module, name } => (Some(*fast), module.clone(), name.clone()),
Lowering::Class { .. } => return Ok(()),
};
let argc = u8::try_from(args.len()).unwrap_or(u8::MAX);
let import = self.cx.import(&module, &func, argc);
let shape = alloc::vec![ValType::Dyn; args.len() + 1];
let win = self.fx.window(&shape);
let mut ts: Vec<Task> = Vec::with_capacity(args.len() * 2 + 1);
for (i, &a) in args.iter().enumerate() {
ts.extend(self.store_expr_tasks(a, FnCx::slot(win.1, i + 1)));
}
ts.push(Task::Step(Step::Intrinsic(IntrinsicStep::Host {
fast,
import,
win,
argc,
dst,
node,
})));
self.sched_all(ts);
Ok(())
}
pub(crate) fn intrinsic_step(&mut self, s: IntrinsicStep) -> Result<(), CompileError> {
match s {
IntrinsicStep::Lsb { op, n, dst, node } => {
self.at(node);
let vals = self.pop_n(n);
let r = self.result_reg(dst);
self.lsb_op(op, &vals, r);
for v in vals {
self.release(v);
}
self.produced(r, dst);
}
IntrinsicStep::Host {
fast,
import,
win,
argc,
dst,
node,
} => {
self.at(node);
let r = self.result_reg(dst);
let args: Vec<Reg> = (0..usize::from(argc))
.map(|i| FnCx::slot(win.1, i + 1))
.collect();
let slow = self.label();
let done = self.label();
if let Some(f) = fast {
self.fast_path(f, &args, r, slow);
self.jmp(done);
}
self.bind(slow);
self.emit(Inst::CallImport {
dst: win.1,
import,
argc,
});
self.emit(Inst::Mov { dst: r, src: win.1 });
self.bind(done);
self.fx.release_window(win.0, win.1);
self.produced(r, dst);
}
}
Ok(())
}
fn lsb_op(&mut self, op: LsbOp, args: &[Val], r: Reg) {
let a = args.first().map_or(Reg(0), |v| v.reg);
let b = args.get(1).map_or(Reg(0), |v| v.reg);
match op {
LsbOp::Concat => self.emit(Inst::DConcat {
dst: r,
lhs: a,
rhs: b,
}),
LsbOp::Len => {
let n = self.fx.temp(ValType::I64);
self.emit(Inst::DLen { dst: n, src: a });
self.emit(Inst::ToDyn {
dst: r,
src: n,
from: BPrim::I64,
});
self.fx.release(n);
}
LsbOp::Truthy | LsbOp::Not => {
let t = self.fx.temp(ValType::Bool);
if op == LsbOp::Truthy {
self.emit(Inst::DTruthy { dst: t, src: a });
} else {
self.emit(Inst::DNot { dst: t, src: a });
}
self.emit(Inst::ToDyn {
dst: r,
src: t,
from: BPrim::Bool,
});
self.fx.release(t);
}
LsbOp::TypeOf => {
let t = self.fx.temp(ValType::U8);
self.emit(Inst::TypeOf { dst: t, src: a });
self.emit(Inst::ToDyn {
dst: r,
src: t,
from: BPrim::U8,
});
self.fx.release(t);
}
LsbOp::Iter => {
if self.php() {
let t = self.fx.temp(ValType::Dyn);
self.dup_into(t, a);
self.emit(Inst::DIterNew { dst: r, src: t });
self.fx.release(t);
} else {
self.emit(Inst::DIterNew { dst: r, src: a });
}
}
LsbOp::IterNext => {
let has = self.fx.temp(ValType::Bool);
let v = self.fx.temp(ValType::Dyn);
let k = self.fx.temp(ValType::Dyn);
let done = self.label();
let end = self.label();
self.emit(Inst::IterNext {
has,
iter: a,
val: v,
});
self.jump_on(has, done, false);
self.emit(Inst::IterKey { dst: k, iter: a });
let m = self.new_map();
self.emit(Inst::MapPush { map: m, src: k });
if self.php() {
self.dup_into(v, v);
}
self.emit(Inst::MapPush { map: m, src: v });
self.emit(Inst::Mov { dst: r, src: m });
self.fx.release(m);
self.jmp(end);
self.bind(done);
self.emit(Inst::LoadNil { dst: r });
self.bind(end);
self.fx.release(k);
self.fx.release(v);
self.fx.release(has);
}
LsbOp::Resume => self.emit(Inst::Resume {
dst: r,
coro: a,
src: b,
}),
LsbOp::ResumeThrow => self.emit(Inst::ResumeThrow {
dst: r,
coro: a,
src: b,
}),
LsbOp::CoroClose => self.emit(Inst::CoroClose {
dst: r,
coro: a,
src: b,
}),
LsbOp::CoroKey => self.emit(Inst::CoroKey { dst: r, coro: a }),
LsbOp::CoroResult => self.emit(Inst::CoroResult { dst: r, coro: a }),
LsbOp::CoroStatus => {
let t = self.fx.temp(ValType::U8);
self.emit(Inst::CoroStatus { dst: t, coro: a });
self.emit(Inst::ToDyn {
dst: r,
src: t,
from: BPrim::U8,
});
self.fx.release(t);
}
LsbOp::ErrCode => {
let t = self.fx.temp(ValType::U32);
self.emit(Inst::ErrCode { dst: t, src: a });
self.emit(Inst::ToDyn {
dst: r,
src: t,
from: BPrim::U32,
});
self.fx.release(t);
}
LsbOp::ErrPayload => self.emit(Inst::ErrPayload { dst: r, src: a }),
}
}
fn need_kind(&mut self, src: Reg, kind: Kind, slow: Label) {
let b = self.fx.temp(ValType::Bool);
self.emit(Inst::IsKind { dst: b, src, kind });
self.jump_on(b, slow, false);
self.fx.release(b);
}
fn need_number(&mut self, src: Reg, slow: Label) {
let b = self.fx.temp(ValType::Bool);
let ok = self.label();
self.emit(Inst::IsKind {
dst: b,
src,
kind: Kind::Int,
});
self.jump_on(b, ok, true);
self.emit(Inst::IsKind {
dst: b,
src,
kind: Kind::Float,
});
self.jump_on(b, slow, false);
self.bind(ok);
self.fx.release(b);
}
fn kind_code(&mut self, src: Reg) -> Reg {
let t = self.fx.temp(ValType::U8);
self.emit(Inst::TypeOf { dst: t, src });
t
}
fn code_cmp(&mut self, b: Reg, t: Reg, k: u8, le: bool) {
let c = self.fx.temp(ValType::U8);
self.emit(Inst::LoadInt {
dst: c,
val: i32::from(k),
ty: IntTy::U8,
});
self.emit(if le {
Inst::ILe {
dst: b,
lhs: t,
rhs: c,
ty: IntTy::U8,
}
} else {
Inst::IEq {
dst: b,
lhs: t,
rhs: c,
ty: IntTy::U8,
}
});
self.fx.release(c);
}
fn need_scalar(&mut self, t: Reg, str_ok: bool, slow: Label) {
let b = self.fx.temp(ValType::Bool);
let ok = self.label();
self.code_cmp(b, t, 3, true);
if str_ok {
self.jump_on(b, ok, true);
self.code_cmp(b, t, 5, false);
}
self.jump_on(b, slow, false);
self.bind(ok);
self.fx.release(b);
}
fn box_bool(&mut self, r: Reg, b: Reg) {
self.emit(Inst::ToDyn {
dst: r,
src: b,
from: BPrim::Bool,
});
}
fn fast_path(&mut self, f: Fast, args: &[Reg], r: Reg, slow: Label) {
let a = args.first().copied().unwrap_or(Reg(0));
let b = args.get(1).copied().unwrap_or(Reg(0));
match f {
Fast::Concat => {
self.need_kind(a, Kind::Str, slow);
self.need_kind(b, Kind::Str, slow);
self.emit(Inst::DConcat {
dst: r,
lhs: a,
rhs: b,
});
}
Fast::ConcatN => self.fast_concat_n(args, r, slow),
Fast::IntRem => {
self.need_kind(a, Kind::Int, slow);
self.need_kind(b, Kind::Int, slow);
let pol = Policy::new().with_div_zero(bytecode_lang::DivZero::Error);
self.emit(Inst::DRem {
dst: r,
lhs: a,
rhs: b,
pol,
});
}
Fast::NumPow => {
self.need_number(a, slow);
self.need_number(b, slow);
let pol = Policy::new().with_overflow(Overflow::Promote);
self.emit(Inst::DPow {
dst: r,
lhs: a,
rhs: b,
pol,
});
}
Fast::IntShl | Fast::IntShr => {
self.need_kind(a, Kind::Int, slow);
self.need_kind(b, Kind::Int, slow);
let pol = Policy::new().with_shift(Shift::Saturate);
self.emit(if f == Fast::IntShl {
Inst::DShl {
dst: r,
lhs: a,
rhs: b,
pol,
}
} else {
Inst::DShr {
dst: r,
lhs: a,
rhs: b,
pol,
}
});
}
Fast::ScalarXor => {
let ta = self.kind_code(a);
let tb = self.kind_code(b);
self.need_scalar(ta, false, slow);
self.need_scalar(tb, false, slow);
let x = self.fx.temp(ValType::Bool);
let y = self.fx.temp(ValType::Bool);
self.emit(Inst::DTruthy { dst: x, src: a });
self.emit(Inst::DTruthy { dst: y, src: b });
self.emit(Inst::BXor {
dst: x,
lhs: x,
rhs: y,
});
self.box_bool(r, x);
for t in [y, x, tb, ta] {
self.fx.release(t);
}
}
Fast::LooseEq | Fast::LooseNe => self.fast_loose_eq(a, b, r, f == Fast::LooseNe, slow),
Fast::StrictEq | Fast::StrictNe => {
self.fast_strict_eq(a, b, r, f == Fast::StrictNe, slow);
}
Fast::Lt | Fast::Le | Fast::Gt | Fast::Ge => self.fast_order(f, a, b, r, slow),
Fast::IntSpaceship => {
self.need_kind(a, Kind::Int, slow);
self.need_kind(b, Kind::Int, slow);
let t = self.fx.temp(ValType::Bool);
let (less, equal, done) = (self.label(), self.label(), self.label());
self.emit(Inst::DLt {
dst: t,
lhs: a,
rhs: b,
});
self.jump_on(t, less, true);
self.emit(Inst::DEq {
dst: t,
lhs: a,
rhs: b,
});
self.jump_on(t, equal, true);
self.emit(Inst::DLoadInt { dst: r, val: 1 });
self.jmp(done);
self.bind(less);
self.emit(Inst::DLoadInt { dst: r, val: -1 });
self.jmp(done);
self.bind(equal);
self.emit(Inst::DLoadInt { dst: r, val: 0 });
self.bind(done);
self.fx.release(t);
}
Fast::NumIdentity => {
self.need_number(a, slow);
self.emit(Inst::Mov { dst: r, src: a });
}
Fast::IntBitNot => {
self.need_kind(a, Kind::Int, slow);
self.emit(Inst::DBitNot {
dst: r,
src: a,
pol: Policy::new(),
});
}
Fast::ToInt | Fast::ToFloat => self.fast_to_number(f == Fast::ToFloat, a, r, slow),
Fast::ToStr => {
self.need_kind(a, Kind::Str, slow);
self.emit(Inst::Mov { dst: r, src: a });
}
Fast::ToBool => {
let t = self.kind_code(a);
self.need_scalar(t, false, slow);
let x = self.fx.temp(ValType::Bool);
self.emit(Inst::DTruthy { dst: x, src: a });
self.box_bool(r, x);
self.fx.release(x);
self.fx.release(t);
}
Fast::ToArray => {
self.need_kind(a, Kind::Map, slow);
self.emit(Inst::Mov { dst: r, src: a });
}
Fast::StrOffset => self.fast_str_offset(a, b, r, slow),
}
}
fn fast_str_offset(&mut self, a: Reg, b: Reg, r: Reg, slow: Label) {
self.need_kind(a, Kind::Str, slow);
self.need_kind(b, Kind::Int, slow);
let s = self.fx.temp(ValType::Str);
let i = self.fx.temp(ValType::I64);
let n = self.fx.temp(ValType::I64);
let z = self.fx.temp(ValType::I64);
let t = self.fx.temp(ValType::Bool);
let op = bytecode_lang::IntOp::new(IntTy::I64);
self.emit(Inst::FromDyn {
dst: s,
src: a,
to: BPrim::Str,
});
self.emit(Inst::FromDyn {
dst: i,
src: b,
to: BPrim::I64,
});
self.emit(Inst::StrLen { dst: n, s });
self.load_i64(z, 0);
let positive = self.label();
self.emit(Inst::ILt {
dst: t,
lhs: i,
rhs: z,
ty: IntTy::I64,
});
self.jump_on(t, positive, false);
self.emit(Inst::IAdd {
dst: i,
lhs: i,
rhs: n,
op,
});
self.emit(Inst::ILt {
dst: t,
lhs: i,
rhs: z,
ty: IntTy::I64,
});
self.jump_on(t, slow, true);
self.bind(positive);
self.emit(Inst::IGe {
dst: t,
lhs: i,
rhs: n,
ty: IntTy::I64,
});
self.jump_on(t, slow, true);
let range = self.fx.window(&[ValType::I64, ValType::I64]);
let one = self.fx.temp(ValType::I64);
self.load_i64(one, 1);
self.emit(Inst::Mov {
dst: FnCx::slot(range.1, 0),
src: i,
});
self.emit(Inst::IAdd {
dst: FnCx::slot(range.1, 1),
lhs: i,
rhs: one,
op,
});
let out = self.fx.temp(ValType::Str);
self.emit(Inst::StrSlice {
dst: out,
s,
range: range.1,
utf8: false,
});
self.emit(Inst::ToDyn {
dst: r,
src: out,
from: BPrim::Str,
});
self.fx.release_window(range.0, range.1);
for x in [out, one, t, z, n, i, s] {
self.fx.release(x);
}
}
pub(crate) fn index_read(&mut self, dst: Reg, obj: Reg, key: Reg) {
let def = self
.cx
.opts
.string_offsets()
.and_then(|n| self.cx.opts.intrinsics().get(n))
.map(|d| d.lowering().clone());
let Some(lowering) = def else {
self.emit(Inst::DGetIndex { dst, obj, key });
return;
};
let (other, done) = (self.label(), self.label());
let b = self.fx.temp(ValType::Bool);
self.emit(Inst::IsKind {
dst: b,
src: obj,
kind: Kind::Str,
});
self.jump_on(b, other, false);
self.fx.release(b);
match &lowering {
Lowering::Lsb(op) => {
let op = *op;
let vals = [
Val {
reg: obj,
rel: super::Rel::Keep,
},
Val {
reg: key,
rel: super::Rel::Keep,
},
];
self.lsb_op(op, &vals, dst);
}
Lowering::Host { module, name } | Lowering::Fast { module, name, .. } => {
let fast = match &lowering {
Lowering::Fast { fast, .. } => Some(*fast),
_ => None,
};
let import = self.cx.import(module, name, 2);
let win = self.fx.window(&[ValType::Dyn, ValType::Dyn, ValType::Dyn]);
let (a1, a2) = (FnCx::slot(win.1, 1), FnCx::slot(win.1, 2));
self.emit(Inst::Mov { dst: a1, src: obj });
self.emit(Inst::Mov { dst: a2, src: key });
let (slow, end) = (self.label(), self.label());
if let Some(f) = fast {
self.fast_path(f, &[a1, a2], dst, slow);
self.jmp(end);
}
self.bind(slow);
self.emit(Inst::CallImport {
dst: win.1,
import,
argc: 2,
});
self.emit(Inst::Mov { dst, src: win.1 });
self.bind(end);
self.fx.release_window(win.0, win.1);
}
_ => self.emit(Inst::DGetIndex { dst, obj, key }),
}
self.jmp(done);
self.bind(other);
self.emit(Inst::DGetIndex { dst, obj, key });
self.bind(done);
}
fn fast_concat_n(&mut self, args: &[Reg], r: Reg, slow: Label) {
for &p in args {
self.need_kind(p, Kind::Str, slow);
}
if args.is_empty() {
let s = self.cx.mb.string("");
let k = self.cx.mb.constant(Const::Str(s));
self.emit(Inst::DLoadConst { dst: r, k });
return;
}
let shape = alloc::vec![ValType::Str; args.len()];
let parts = self.fx.window(&shape);
for (i, &p) in args.iter().enumerate() {
self.emit(Inst::FromDyn {
dst: FnCx::slot(parts.1, i),
src: p,
to: BPrim::Str,
});
}
let s = self.fx.temp(ValType::Str);
self.emit(Inst::StrConcatN {
dst: s,
first: parts.1,
count: u8::try_from(args.len()).unwrap_or(u8::MAX),
});
self.emit(Inst::ToDyn {
dst: r,
src: s,
from: BPrim::Str,
});
self.fx.release(s);
self.fx.release_window(parts.0, parts.1);
}
fn fast_loose_eq(&mut self, a: Reg, b: Reg, r: Reg, negate: bool, slow: Label) {
let ta = self.kind_code(a);
let tb = self.kind_code(b);
let t = self.fx.temp(ValType::Bool);
let (strings, done) = (self.label(), self.label());
self.emit(Inst::IEq {
dst: t,
lhs: ta,
rhs: tb,
ty: IntTy::U8,
});
self.jump_on(t, slow, false);
self.code_cmp(t, ta, 3, true);
self.jump_on(t, strings, false);
self.emit(Inst::DEq {
dst: t,
lhs: a,
rhs: b,
});
if negate {
self.emit(Inst::BNot { dst: t, src: t });
}
self.box_bool(r, t);
self.jmp(done);
self.bind(strings);
self.code_cmp(t, ta, 5, false);
self.jump_on(t, slow, false);
let sa = self.fx.temp(ValType::Str);
let sb = self.fx.temp(ValType::Str);
self.emit(Inst::FromDyn {
dst: sa,
src: a,
to: BPrim::Str,
});
self.emit(Inst::FromDyn {
dst: sb,
src: b,
to: BPrim::Str,
});
self.emit(Inst::StrEq {
dst: t,
lhs: sa,
rhs: sb,
});
self.jump_on(t, slow, false);
self.emit(Inst::LoadBool {
dst: t,
val: !negate,
});
self.box_bool(r, t);
self.bind(done);
for x in [sb, sa, t, tb, ta] {
self.fx.release(x);
}
}
fn fast_strict_eq(&mut self, a: Reg, b: Reg, r: Reg, negate: bool, slow: Label) {
let ta = self.kind_code(a);
let tb = self.kind_code(b);
self.need_scalar(ta, true, slow);
self.need_scalar(tb, true, slow);
let t = self.fx.temp(ValType::Bool);
let (differ, done) = (self.label(), self.label());
self.emit(Inst::IEq {
dst: t,
lhs: ta,
rhs: tb,
ty: IntTy::U8,
});
self.jump_on(t, differ, false);
self.emit(Inst::DEq {
dst: t,
lhs: a,
rhs: b,
});
if negate {
self.emit(Inst::BNot { dst: t, src: t });
}
self.box_bool(r, t);
self.jmp(done);
self.bind(differ);
self.emit(Inst::LoadBool {
dst: t,
val: negate,
});
self.box_bool(r, t);
self.bind(done);
for x in [t, tb, ta] {
self.fx.release(x);
}
}
fn fast_order(&mut self, f: Fast, a: Reg, b: Reg, r: Reg, slow: Label) {
let ta = self.kind_code(a);
let tb = self.kind_code(b);
let t = self.fx.temp(ValType::Bool);
let num = self.label();
self.emit(Inst::IEq {
dst: t,
lhs: ta,
rhs: tb,
ty: IntTy::U8,
});
self.jump_on(t, slow, false);
self.code_cmp(t, ta, 2, false);
self.jump_on(t, num, true);
self.code_cmp(t, ta, 3, false);
self.jump_on(t, slow, false);
self.bind(num);
self.emit(match f {
Fast::Lt => Inst::DLt {
dst: t,
lhs: a,
rhs: b,
},
Fast::Le => Inst::DLe {
dst: t,
lhs: a,
rhs: b,
},
Fast::Gt => Inst::DGt {
dst: t,
lhs: a,
rhs: b,
},
_ => Inst::DGe {
dst: t,
lhs: a,
rhs: b,
},
});
self.box_bool(r, t);
for x in [t, tb, ta] {
self.fx.release(x);
}
}
fn fast_to_number(&mut self, float: bool, a: Reg, r: Reg, slow: Label) {
if !float {
return self.fast_to_int(a, r, slow);
}
let t = self.fx.temp(ValType::Bool);
let (own, other, boolean, nil, done) = (
self.label(),
self.label(),
self.label(),
self.label(),
self.label(),
);
let (same, cross) = if float {
(Kind::Float, Kind::Int)
} else {
(Kind::Int, Kind::Float)
};
self.emit(Inst::IsKind {
dst: t,
src: a,
kind: same,
});
self.jump_on(t, own, true);
self.emit(Inst::IsKind {
dst: t,
src: a,
kind: Kind::Bool,
});
self.jump_on(t, boolean, true);
self.emit(Inst::IsKind {
dst: t,
src: a,
kind: Kind::Nil,
});
self.jump_on(t, nil, true);
if float {
self.emit(Inst::IsKind {
dst: t,
src: a,
kind: cross,
});
self.jump_on(t, other, true);
}
self.jmp(slow);
self.bind(own);
self.emit(Inst::Mov { dst: r, src: a });
self.jmp(done);
let i = self.fx.temp(ValType::I64);
if float {
self.bind(other);
self.emit(Inst::FromDyn {
dst: i,
src: a,
to: BPrim::I64,
});
self.int_to_dyn_float(r, i);
self.jmp(done);
}
self.bind(boolean);
self.emit(Inst::FromDyn {
dst: t,
src: a,
to: BPrim::Bool,
});
self.emit(Inst::BoolToInt {
dst: i,
src: t,
ty: IntTy::I64,
});
if float {
self.int_to_dyn_float(r, i);
} else {
self.emit(Inst::ToDyn {
dst: r,
src: i,
from: BPrim::I64,
});
}
self.jmp(done);
self.bind(nil);
if float {
let k = self.cx.mb.constant(Const::f64(0.0));
self.emit(Inst::DLoadConst { dst: r, k });
} else {
self.emit(Inst::DLoadInt { dst: r, val: 0 });
}
self.bind(done);
self.fx.release(i);
self.fx.release(t);
}
fn fast_to_int(&mut self, a: Reg, r: Reg, slow: Label) {
let t = self.fx.temp(ValType::Bool);
let (own, flt, boolean, nil, done) = (
self.label(),
self.label(),
self.label(),
self.label(),
self.label(),
);
for (kind, l) in [
(Kind::Int, own),
(Kind::Float, flt),
(Kind::Bool, boolean),
(Kind::Nil, nil),
] {
self.emit(Inst::IsKind {
dst: t,
src: a,
kind,
});
self.jump_on(t, l, true);
}
self.jmp(slow);
self.bind(own);
self.emit(Inst::Mov { dst: r, src: a });
self.jmp(done);
let i = self.fx.temp(ValType::I64);
self.bind(boolean);
self.emit(Inst::FromDyn {
dst: t,
src: a,
to: BPrim::Bool,
});
self.emit(Inst::BoolToInt {
dst: i,
src: t,
ty: IntTy::I64,
});
self.emit(Inst::ToDyn {
dst: r,
src: i,
from: BPrim::I64,
});
self.jmp(done);
self.bind(nil);
self.emit(Inst::DLoadInt { dst: r, val: 0 });
self.jmp(done);
self.bind(flt);
self.float_to_lval(a, i);
self.emit(Inst::ToDyn {
dst: r,
src: i,
from: BPrim::I64,
});
self.bind(done);
self.fx.release(i);
self.fx.release(t);
}
fn float_to_lval(&mut self, a: Reg, i: Reg) {
let f = self.fx.temp(ValType::F64);
let lim = self.fx.temp(ValType::F64);
let t = self.fx.temp(ValType::Bool);
let (out, done) = (self.label(), self.label());
self.emit(Inst::FromDyn {
dst: f,
src: a,
to: BPrim::F64,
});
let k = self.cx.mb.constant(Const::f64(9_223_372_036_854_775_808.0));
self.emit(Inst::LoadConst { dst: lim, k });
self.emit(Inst::FGe {
dst: t,
lhs: f,
rhs: lim,
ty: bytecode_lang::FloatTy::F64,
});
self.jump_on(t, out, true);
let k = self
.cx
.mb
.constant(Const::f64(-9_223_372_036_854_775_808.0));
self.emit(Inst::LoadConst { dst: lim, k });
self.emit(Inst::FLt {
dst: t,
lhs: f,
rhs: lim,
ty: bytecode_lang::FloatTy::F64,
});
self.jump_on(t, out, true);
self.emit(Inst::F64ToInt {
dst: i,
src: f,
conv: bytecode_lang::FloatConv::new(IntTy::I64)
.with_float_to_int(bytecode_lang::FloatToInt::Saturate),
});
self.jmp(done);
self.bind(out);
let bits = self.fx.temp(ValType::I64);
let e = self.fx.temp(ValType::I64);
let c = self.fx.temp(ValType::I64);
let wrap = bytecode_lang::IntOp::new(IntTy::I64)
.with_policy(Policy::new().with_overflow(Overflow::Wrap));
let mask = bytecode_lang::IntOp::new(IntTy::I64)
.with_policy(Policy::new().with_shift(Shift::Mask));
self.emit(Inst::FloatToBits {
dst: bits,
src: f,
ty: IntTy::I64,
});
self.load_i64(c, 52);
self.emit(Inst::IShr {
dst: e,
lhs: bits,
rhs: c,
op: mask,
});
self.load_i64(c, 0x7FF);
self.emit(Inst::IAnd {
dst: e,
lhs: e,
rhs: c,
op: wrap,
});
self.load_i64(i, 0);
self.emit(Inst::IEq {
dst: t,
lhs: e,
rhs: c,
ty: IntTy::I64,
});
self.jump_on(t, done, true);
self.load_i64(c, 1075);
self.emit(Inst::ISub {
dst: e,
lhs: e,
rhs: c,
op: wrap,
});
self.load_i64(c, 64);
self.emit(Inst::IGe {
dst: t,
lhs: e,
rhs: c,
ty: IntTy::I64,
});
self.jump_on(t, done, true);
let m = self.fx.temp(ValType::I64);
self.load_i64(c, (1i64 << 52) - 1);
self.emit(Inst::IAnd {
dst: m,
lhs: bits,
rhs: c,
op: wrap,
});
self.load_i64(c, 1i64 << 52);
self.emit(Inst::IOr {
dst: m,
lhs: m,
rhs: c,
op: wrap,
});
self.emit(Inst::IShl {
dst: i,
lhs: m,
rhs: e,
op: mask,
});
self.load_i64(c, 0);
self.emit(Inst::ILt {
dst: t,
lhs: bits,
rhs: c,
ty: IntTy::I64,
});
self.jump_on(t, done, false);
self.emit(Inst::INeg {
dst: i,
src: i,
op: wrap,
});
self.bind(done);
for x in [m, c, e, bits, t, lim, f] {
self.fx.release(x);
}
}
fn int_to_dyn_float(&mut self, r: Reg, i: Reg) {
let f = self.fx.temp(ValType::F64);
self.emit(Inst::IntToF64 {
dst: f,
src: i,
ty: IntTy::I64,
});
self.emit(Inst::ToDyn {
dst: r,
src: f,
from: BPrim::F64,
});
self.fx.release(f);
}
}