use std::collections::HashMap;
use std::fmt::{self, Display, Formatter};
use std::sync::Arc;
use std::sync::atomic::{AtomicU8, AtomicU16, Ordering};
use parking_lot::Mutex;
use super::enum_def::EnumDef;
use super::typeir::{CastIr, TypeIr};
pub type Reg = u16;
#[derive(Clone)]
pub enum Const {
Big(i128, super::numeric::IntWidth),
Float(f64),
F32(f32),
Char(char),
Str(Arc<str>),
Bytes(Arc<[u8]>),
}
pub const DISCARD: Reg = Reg::MAX;
#[derive(Clone, Copy, Debug)]
pub enum BinKind {
Add,
Sub,
Mul,
Div,
Rem,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
BitAnd,
BitOr,
BitXor,
Shl,
Shr,
}
pub fn overflow_message(op: BinKind) -> &'static str {
match op {
BinKind::Add => "attempt to add with overflow",
BinKind::Sub => "attempt to subtract with overflow",
BinKind::Mul => "attempt to multiply with overflow",
BinKind::Div => "attempt to divide with overflow",
BinKind::Rem => "attempt to calculate the remainder with overflow",
_ => "attempt to compute with overflow",
}
}
#[derive(Clone, Copy, Debug)]
pub enum UnKind {
Neg,
Not,
}
#[derive(Clone)]
pub enum Member {
Named(FieldName),
Indexed(usize),
}
pub struct FieldName {
pub name: Arc<str>,
slot: AtomicU16,
}
impl FieldName {
pub fn new(name: Arc<str>) -> FieldName {
FieldName {
name,
slot: AtomicU16::new(u16::MAX),
}
}
pub fn slot_in(&self, shape: &StructShape) -> Option<usize> {
let hint = self.slot.load(Ordering::Relaxed);
if let Some(field) = shape.fields.get(usize::from(hint))
&& (Arc::ptr_eq(field, &self.name) || **field == *self.name)
{
return Some(usize::from(hint));
}
let found = shape.slot(&self.name)?;
if let Ok(small) = u16::try_from(found) {
self.slot.store(small, Ordering::Relaxed);
}
Some(found)
}
}
impl Clone for FieldName {
fn clone(&self) -> FieldName {
FieldName {
name: self.name.clone(),
slot: AtomicU16::new(self.slot.load(Ordering::Relaxed)),
}
}
}
impl Display for FieldName {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(&self.name)
}
}
#[derive(Clone, Copy)]
pub enum CapSource {
Local(Reg),
Upvalue(u16),
MutableLocal(Reg),
MutableUpvalue(u16),
}
impl CapSource {
pub fn is_mutable(self) -> bool {
matches!(self, Self::MutableLocal(_) | Self::MutableUpvalue(_))
}
}
pub struct StructLit {
pub shape: Arc<StructShape>,
pub has_rest: bool,
}
#[derive(Clone)]
pub struct EnumVariant {
pub def: Arc<EnumDef>,
pub variant: u16,
}
pub const NO_TYPE: u16 = u16::MAX;
pub const NO_ATOM: u32 = u32::MAX;
#[derive(Clone)]
pub struct MethodName {
pub text: String,
pub id: BuiltinId,
pub atom: u32,
pub scalar: Option<ScalarTy>,
}
impl MethodName {
pub fn builtin(id: BuiltinId) -> MethodName {
MethodName {
text: id.name().to_string(),
id,
atom: NO_ATOM,
scalar: None,
}
}
}
impl Display for MethodName {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(&self.text)
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ScalarTy {
Int(super::numeric::IntWidth),
F32,
F64,
Bool,
Char,
Str,
Opt(Box<ScalarTy>),
List(Box<ScalarTy>),
Map(Box<ScalarTy>),
Set(Box<ScalarTy>),
Other,
}
impl ScalarTy {
pub fn lower(ty: &syn::Type) -> Option<Self> {
let syn::Type::Path(path) = ty else {
return None;
};
Self::lower_segment(path.path.segments.last()?)
}
pub fn lower_segment(segment: &syn::PathSegment) -> Option<Self> {
if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
let inner = || Box::new(Self::first_arg(args).unwrap_or(Self::Other));
return match segment.ident.to_string().as_str() {
"Option" => Some(Self::Opt(inner())),
"Vec" | "VecDeque" => Some(Self::List(inner())),
"HashMap" | "BTreeMap" => Some(Self::Map(Box::new(
Self::nth_arg(args, 1).unwrap_or(Self::Other),
))),
"HashSet" | "BTreeSet" => Some(Self::Set(inner())),
_ => None,
};
}
Some(match segment.ident.to_string().as_str() {
"f32" => Self::F32,
"f64" => Self::F64,
"bool" => Self::Bool,
"char" => Self::Char,
"String" | "str" => Self::Str,
name => Self::Int(super::numeric::IntWidth::parse(name)?),
})
}
fn first_arg(args: &syn::AngleBracketedGenericArguments) -> Option<Self> {
Self::nth_arg(args, 0)
}
fn nth_arg(args: &syn::AngleBracketedGenericArguments, n: usize) -> Option<Self> {
args.args
.iter()
.filter_map(|arg| match arg {
syn::GenericArgument::Type(ty) => Some(ty),
_ => None,
})
.nth(n)
.and_then(Self::lower)
}
pub fn payload(&self) -> Option<&ScalarTy> {
match self {
Self::Opt(inner) | Self::List(inner) => Some(inner),
_ => None,
}
}
}
include!(concat!(env!("OUT_DIR"), "/builtin_id.rs"));
include!(concat!(env!("OUT_DIR"), "/path_id.rs"));
#[derive(Clone)]
pub struct PathRef {
pub id: PathId,
pub segs: Vec<String>,
pub coerce: Option<TypeIr>,
}
impl PathRef {
pub fn new(segs: Vec<String>, coerce: Option<TypeIr>) -> Self {
PathRef {
id: PathId::resolve(&segs),
segs,
coerce,
}
}
pub fn user(segs: Vec<String>, coerce: Option<TypeIr>) -> Self {
PathRef {
id: PathId::Other,
segs,
coerce,
}
}
pub fn display(&self) -> String {
self.segs.join("::")
}
}
impl BuiltinId {
pub fn receivers(self) -> &'static [&'static str] {
use BuiltinId::{
AndThen, Chars, Clone, CloneFrom, Concat, Contains, ContainsKey, Copied, EndsWith,
Entry, Filter, First, Get, Insert, IsEmpty, Iter, IterMut, Join, Keys, Last, Len,
Lines, Map, MapOr, OkOrElse, Parse, Pop, Push, PushStr, Remove, Retain, Sort, SortBy,
SortByCachedKey, SortByKey, Split, SplitFirst, SplitWhitespace, StartsWith, Take,
ToString, Trim, Unwrap, UnwrapOr, UnwrapOrElse, Values,
};
match self {
Clone | ToString | CloneFrom => &["*"],
Len | IsEmpty | Get => &["Str", "Vec", "Map"],
Insert | Remove => &["Vec", "Map"],
ContainsKey | Entry | Keys | Values => &["Map"],
Iter => &["Vec", "Map", "Option"],
IterMut | Pop | First | Last | SplitFirst | Sort | SortByKey | SortByCachedKey
| SortBy | Join | Concat | Retain => &["Vec"],
Push | Contains => &["Str", "Vec"],
PushStr | SplitWhitespace | Split | Chars | Lines | Trim | StartsWith | EndsWith
| Parse => &["Str"],
Take | Unwrap | UnwrapOr | UnwrapOrElse | Copied | Map | Filter | AndThen | MapOr
| OkOrElse => &["Option"],
_ => &[],
}
}
pub fn is_higher_order(self) -> bool {
use BuiltinId::{
Chars, Clone, CloneFrom, Cloned, Concat, Contains, ContainsKey, Copied, Count,
EndsWith, Entry, Enumerate, First, FirstMut, Get, GetMut, Insert, IntoIter, IntoKeys,
IntoValues, IsEmpty, Iter, IterMut, Join, Keys, Last, LastMut, Len, Lines, Parse, Pop,
Product, Push, PushStr, Remove, Rev, Skip, Sort, SortUnstable, Split, SplitFirst,
SplitWhitespace, StartsWith, Sum, Take, ToString, Trim, Unwrap, UnwrapOr, Values,
};
!matches!(
self,
Len | IsEmpty
| Clone
| ToString
| Get
| GetMut
| Insert
| ContainsKey
| Remove
| Entry
| Keys
| IntoKeys
| Values
| IntoValues
| Iter
| IntoIter
| IterMut
| Push
| Pop
| First
| FirstMut
| Last
| LastMut
| SplitFirst
| Contains
| Sort
| SortUnstable
| Join
| Concat
| Sum
| Product
| Enumerate
| Rev
| Count
| Take
| Skip
| PushStr
| CloneFrom
| SplitWhitespace
| Split
| Chars
| Lines
| Trim
| StartsWith
| EndsWith
| Parse
| Unwrap
| UnwrapOr
| Copied
| Cloned
)
}
}
#[derive(Clone)]
pub struct FmtSpec {
pub template: String,
pub positional: Vec<Reg>,
pub named: Vec<(String, Reg)>,
}
pub struct PatInfo {
pub pat: PPat,
pub binds: Vec<(String, Reg)>,
}
#[derive(Clone)]
pub struct PTag {
pub name: Option<Arc<str>>,
pub variant: Option<(Arc<EnumDef>, u16)>,
}
impl PTag {
pub fn matches(&self, def: &Arc<EnumDef>, variant: u16) -> bool {
match &self.variant {
Some((want, index)) => EnumDef::same(want, def) && *index == variant,
None => self.name.as_deref() == Some(&**def.variant_name(variant)),
}
}
pub fn is_named(&self, name: &str) -> bool {
self.name.as_deref() == Some(name)
}
}
#[derive(Clone)]
pub enum PLit {
Int(i64),
Float(f64),
Bool(bool),
Str(String),
Char(char),
}
#[derive(Clone)]
pub enum PPat {
Wild,
Rest,
Ident {
name: String,
sub: Option<Box<PPat>>,
},
Lit(PLit),
Tuple(Vec<PPat>),
TupleStruct {
tag: PTag,
elems: Vec<PPat>,
},
Path {
tag: PTag,
},
Struct {
name: Option<String>,
fields: Vec<(String, PPat)>,
},
Or(Vec<PPat>),
Slice(Vec<PPat>),
Range {
lo: Option<PLit>,
hi: Option<PLit>,
inclusive: bool,
},
Unsupported,
}
#[derive(Clone, Copy)]
pub enum MacroKind {
Println,
Print,
Eprintln,
Eprint,
Panic,
Anyhow,
Bail,
}
#[derive(Clone)]
pub enum Op {
LoadConst {
dst: Reg,
k: u16,
},
LoadInt {
dst: Reg,
v: i64,
},
LoadIntW {
dst: Reg,
v: i64,
w: super::numeric::IntWidth,
},
LoadBool {
dst: Reg,
v: bool,
},
LoadUnit {
dst: Reg,
},
LoadUpvalue {
dst: Reg,
idx: u16,
},
LoadCell {
dst: Reg,
cell: Reg,
},
StoreCell {
cell: Reg,
src: Reg,
},
DropCell {
cell: Reg,
},
StoreUpvalue {
idx: u16,
src: Reg,
},
LoadGlobal {
dst: Reg,
idx: u32,
},
Move {
dst: Reg,
src: Reg,
},
Bin {
dst: Reg,
a: Reg,
b: Reg,
op: BinKind,
},
BinImm {
dst: Reg,
a: Reg,
imm: i64,
op: BinKind,
},
Un {
dst: Reg,
a: Reg,
op: UnKind,
},
Jump {
to: u32,
},
LoopHead {
jump: u32,
},
JumpIfFalse {
cond: Reg,
to: u32,
},
JumpIfTrue {
cond: Reg,
to: u32,
},
CmpJump {
a: Reg,
b: Reg,
op: BinKind,
to: u32,
},
CmpJumpImm {
a: Reg,
imm: i64,
op: BinKind,
to: u32,
},
CallFn {
dst: Reg,
func: u32,
base: Reg,
argc: u16,
targ: u32,
},
CallValue {
dst: Reg,
callee: Reg,
base: Reg,
argc: u16,
},
CallPath {
dst: Reg,
path: u16,
base: Reg,
argc: u16,
},
PathValue {
dst: Reg,
path: u16,
},
Method {
dst: Reg,
recv: Reg,
name: u16,
base: Reg,
argc: u16,
},
GetOrDefault {
dst: Reg,
recv: Reg,
key: Reg,
default: Reg,
},
Ret {
src: Reg,
},
MakeVec {
dst: Reg,
base: Reg,
count: u16,
},
MakeMap {
dst: Reg,
set: bool,
},
MakeTuple {
dst: Reg,
base: Reg,
count: u16,
},
MakeArrayRepeat {
dst: Reg,
val: Reg,
count: Reg,
},
MakeRange {
dst: Reg,
start: Reg,
end: Reg,
inclusive: bool,
},
IterInit {
dst: Reg,
src: Reg,
},
ForNext {
iter: Reg,
idx: Reg,
val: Reg,
to: u32,
},
MakeStruct {
dst: Reg,
info: u16,
base: Reg,
},
MakeEnum {
dst: Reg,
info: u16,
base: Reg,
count: u16,
},
LoadEnum {
dst: Reg,
info: u16,
},
MakeClosure {
dst: Reg,
child: u16,
},
Index {
dst: Reg,
base: Reg,
key: Reg,
},
SetIndex {
base: Reg,
key: Reg,
val: Reg,
},
Deref {
dst: Reg,
src: Reg,
},
SetDeref {
target: Reg,
val: Reg,
},
DerefBinAssign {
target: Reg,
val: Reg,
op: BinKind,
},
SetDerefParam {
target: Reg,
val: Reg,
},
GetField {
dst: Reg,
base: Reg,
member: u16,
},
SetField {
base: Reg,
member: u16,
val: Reg,
},
UniqueReg {
reg: Reg,
},
UniqueField {
dst: Reg,
base: Reg,
member: u16,
},
UniqueIndex {
dst: Reg,
base: Reg,
key: Reg,
},
UniqueCell {
dst: Reg,
cell: Reg,
},
UniqueUpvalue {
dst: Reg,
idx: u16,
},
RefIndex {
dst: Reg,
base: Reg,
key: Reg,
},
RefField {
dst: Reg,
base: Reg,
member: u16,
},
MakeBorrow {
dst: Reg,
src: Reg,
},
DefaultOf {
dst: Reg,
src: Reg,
},
DropScope {
list: u16,
},
MoveOut {
src: Reg,
},
Try {
dst: Reg,
src: Reg,
},
TryJump {
dst: Reg,
src: Reg,
to: u32,
},
Cast {
dst: Reg,
src: Reg,
ty: u16,
},
Coerce {
dst: Reg,
src: Reg,
ty: u16,
},
TestBind {
val: Reg,
pat: u16,
dst: Reg,
},
Fmt {
dst: Reg,
spec: u16,
},
MacroCall {
kind: MacroKind,
dst: Reg,
spec: u16,
},
Dbg {
dst: Reg,
base: Reg,
argc: u16,
},
Spawn {
dst: Reg,
child: u16,
},
Await {
dst: Reg,
src: Reg,
},
}
pub struct Chunk {
pub code: Vec<Op>,
pub lines: Vec<u32>,
pub file: Arc<str>,
pub num_regs: usize,
pub num_params: usize,
pub param_types: Vec<Option<String>>,
pub name: String,
pub module: u16,
pub consts: Vec<Const>,
pub members: Vec<Member>,
pub pats: Vec<PatInfo>,
pub fmts: Vec<FmtSpec>,
pub struct_lits: Vec<StructLit>,
pub enum_variants: Vec<EnumVariant>,
pub casts: Vec<CastIr>,
pub coerces: Vec<TypeIr>,
pub paths: Vec<PathRef>,
pub names: Vec<MethodName>,
pub children: Vec<Arc<Chunk>>,
pub child_caps: Vec<Vec<CapSource>>,
pub generics: Vec<Arc<str>>,
pub drop_lists: Vec<Arc<[Reg]>>,
pub call_type_args: Vec<Arc<[TypeIr]>>,
pub path_forwarder: bool,
pub loop_plans: Mutex<HashMap<usize, Option<Arc<super::scalar_loop::LoopPlan>>>>,
pub while_plans: Mutex<HashMap<usize, Arc<super::scalar_while::WhilePlan>>>,
pub while_rejected: Vec<AtomicU8>,
pub fn_plan: Mutex<Option<Arc<super::scalar_fn::FnPlan>>>,
pub fn_rejected: AtomicU8,
}
impl Chunk {
pub fn empty(name: impl Into<String>) -> Chunk {
Chunk {
code: Vec::new(),
lines: Vec::new(),
file: Arc::from(""),
num_regs: 0,
num_params: 0,
param_types: Vec::new(),
name: name.into(),
module: 0,
consts: Vec::new(),
members: Vec::new(),
pats: Vec::new(),
fmts: Vec::new(),
struct_lits: Vec::new(),
enum_variants: Vec::new(),
casts: Vec::new(),
coerces: Vec::new(),
paths: Vec::new(),
names: Vec::new(),
children: Vec::new(),
child_caps: Vec::new(),
generics: Vec::new(),
drop_lists: Vec::new(),
call_type_args: Vec::new(),
path_forwarder: false,
loop_plans: Mutex::new(HashMap::new()),
while_plans: Mutex::new(HashMap::new()),
while_rejected: Vec::new(),
fn_plan: Mutex::new(None),
fn_rejected: AtomicU8::new(0),
}
}
}
pub struct StructShape {
pub name: Arc<str>,
pub type_id: u16,
pub fields: Vec<Arc<str>>,
pub renames: Vec<Option<Arc<str>>>,
}
impl StructShape {
pub fn new(name: impl Into<Arc<str>>, fields: Vec<Arc<str>>) -> Arc<StructShape> {
Arc::new(StructShape {
name: name.into(),
type_id: NO_TYPE,
fields,
renames: Vec::new(),
})
}
pub fn typed(
name: impl Into<Arc<str>>,
type_id: u16,
fields: Vec<Arc<str>>,
renames: Vec<Option<Arc<str>>>,
) -> Arc<StructShape> {
Arc::new(StructShape {
name: name.into(),
type_id,
fields,
renames,
})
}
pub fn slot(&self, field: &str) -> Option<usize> {
self.fields.iter().position(|f| &**f == field)
}
}
pub fn path_call_chunk(path: PathRef, num_params: usize) -> Arc<Chunk> {
let count = u16::try_from(num_params).expect("parameter count fits u16");
let dst = count * 2;
let mut chunk = Chunk::empty("<pathfn>");
chunk.path_forwarder = true;
chunk.num_params = num_params;
chunk.num_regs = num_params * 2 + 1;
chunk.paths.push(path);
for i in 0..count {
chunk.code.push(Op::Move {
dst: count + i,
src: i,
});
}
chunk.code.push(Op::CallPath {
dst,
path: 0,
base: count,
argc: count,
});
chunk.code.push(Op::Ret { src: dst });
Arc::new(chunk)
}