mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-14 07:45:04 +00:00
348 lines
10 KiB
Rust
348 lines
10 KiB
Rust
use crate::symbol::Symbol;
|
|
|
|
/// Low-level operations that get translated directly into e.g. LLVM instructions.
|
|
/// These are always wrapped when exposed to end users, and can only make it
|
|
/// into an Expr when added directly by can::builtins
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
pub enum LowLevel {
|
|
StrConcat,
|
|
StrJoinWith,
|
|
StrIsEmpty,
|
|
StrStartsWith,
|
|
StrStartsWithScalar,
|
|
StrEndsWith,
|
|
StrSplit,
|
|
StrCountGraphemes,
|
|
StrCountUtf8Bytes,
|
|
StrFromInt,
|
|
StrFromUtf8Range,
|
|
StrToUtf8,
|
|
StrRepeat,
|
|
StrFromFloat,
|
|
StrTrim,
|
|
StrTrimLeft,
|
|
StrTrimRight,
|
|
StrToNum,
|
|
StrToScalars,
|
|
StrGetUnsafe,
|
|
StrSubstringUnsafe,
|
|
StrReserve,
|
|
StrAppendScalar,
|
|
StrGetScalarUnsafe,
|
|
StrGetCapacity,
|
|
ListLen,
|
|
ListWithCapacity,
|
|
ListReserve,
|
|
ListAppendUnsafe,
|
|
ListGetUnsafe,
|
|
ListReplaceUnsafe,
|
|
ListConcat,
|
|
ListPrepend,
|
|
ListMap,
|
|
ListMap2,
|
|
ListMap3,
|
|
ListMap4,
|
|
ListSortWith,
|
|
ListSublist,
|
|
ListDropAt,
|
|
ListSwap,
|
|
ListIsUnique,
|
|
ListGetCapacity,
|
|
DictSize,
|
|
DictEmpty,
|
|
DictInsert,
|
|
DictRemove,
|
|
DictContains,
|
|
DictGetUnsafe,
|
|
DictKeys,
|
|
DictValues,
|
|
DictUnion,
|
|
DictIntersection,
|
|
DictDifference,
|
|
DictWalk,
|
|
DictGetCapacity,
|
|
SetFromList,
|
|
SetToDict,
|
|
SetGetCapacity,
|
|
NumAdd,
|
|
NumAddWrap,
|
|
NumAddChecked,
|
|
NumAddSaturated,
|
|
NumSub,
|
|
NumSubWrap,
|
|
NumSubChecked,
|
|
NumSubSaturated,
|
|
NumMul,
|
|
NumMulWrap,
|
|
NumMulSaturated,
|
|
NumMulChecked,
|
|
NumGt,
|
|
NumGte,
|
|
NumLt,
|
|
NumLte,
|
|
NumCompare,
|
|
NumDivUnchecked,
|
|
NumDivCeilUnchecked,
|
|
NumRemUnchecked,
|
|
NumIsMultipleOf,
|
|
NumAbs,
|
|
NumNeg,
|
|
NumSin,
|
|
NumCos,
|
|
NumSqrtUnchecked,
|
|
NumLogUnchecked,
|
|
NumRound,
|
|
NumToFrac,
|
|
NumPow,
|
|
NumCeiling,
|
|
NumPowInt,
|
|
NumFloor,
|
|
NumIsFinite,
|
|
NumAtan,
|
|
NumAcos,
|
|
NumAsin,
|
|
NumBytesToU16,
|
|
NumBytesToU32,
|
|
NumBitwiseAnd,
|
|
NumBitwiseXor,
|
|
NumBitwiseOr,
|
|
NumShiftLeftBy,
|
|
NumShiftRightBy,
|
|
NumShiftRightZfBy,
|
|
NumIntCast,
|
|
NumToFloatCast,
|
|
NumToIntChecked,
|
|
NumToFloatChecked,
|
|
NumToStr,
|
|
Eq,
|
|
NotEq,
|
|
And,
|
|
Or,
|
|
Not,
|
|
Hash,
|
|
PtrCast,
|
|
RefCountInc,
|
|
RefCountDec,
|
|
BoxExpr,
|
|
UnboxExpr,
|
|
Unreachable,
|
|
}
|
|
|
|
macro_rules! higher_order {
|
|
() => {
|
|
ListMap | ListMap2 | ListMap3 | ListMap4 | ListSortWith | DictWalk
|
|
};
|
|
}
|
|
|
|
impl LowLevel {
|
|
/// is one of the arguments always a function?
|
|
/// An example is List.map.
|
|
pub fn is_higher_order(&self) -> bool {
|
|
use LowLevel::*;
|
|
|
|
matches!(self, higher_order!())
|
|
}
|
|
|
|
pub fn function_argument_position(&self) -> usize {
|
|
use LowLevel::*;
|
|
|
|
match self {
|
|
ListMap => 1,
|
|
ListMap2 => 2,
|
|
ListMap3 => 3,
|
|
ListMap4 => 4,
|
|
ListSortWith => 1,
|
|
DictWalk => 2,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Some wrapper functions can just be replaced by lowlevels in the backend for performance.
|
|
/// For example, Num.add should be an instruction, not a function call.
|
|
/// Variant names are chosen to help explain what to do when adding new lowlevels
|
|
pub enum LowLevelWrapperType {
|
|
/// This wrapper function contains no logic and we can remove it in code gen
|
|
CanBeReplacedBy(LowLevel),
|
|
NotALowLevelWrapper,
|
|
}
|
|
|
|
impl LowLevelWrapperType {
|
|
pub fn from_symbol(symbol: Symbol) -> LowLevelWrapperType {
|
|
for_symbol_help(symbol)
|
|
}
|
|
}
|
|
|
|
/// We use a rust macro to ensure that every LowLevel gets handled
|
|
macro_rules! map_symbol_to_lowlevel {
|
|
($($lowlevel:ident <= $symbol:ident),* $(,)?) => {
|
|
|
|
fn for_symbol_help(symbol: Symbol) -> LowLevelWrapperType {
|
|
use $crate::low_level::LowLevelWrapperType::*;
|
|
|
|
// expands to a big (but non-exhaustive) match on symbols and maps them to a lowlevel
|
|
match symbol {
|
|
$(
|
|
Symbol::$symbol => CanBeReplacedBy(LowLevel::$lowlevel),
|
|
)*
|
|
|
|
_ => NotALowLevelWrapper,
|
|
}
|
|
}
|
|
|
|
fn _enforce_exhaustiveness(lowlevel: LowLevel) -> Symbol {
|
|
// when adding a new lowlevel, this match will stop being exhaustive, and give a
|
|
// compiler error. Most likely, you are adding a new lowlevel that maps directly to a
|
|
// symbol. For instance, you want to have `List.foo` to stand for the `ListFoo`
|
|
// lowlevel. In that case, see below in the invocation of `map_symbol_to_lowlevel!`
|
|
//
|
|
// Below, we explicitly handle some exceptions to the pattern where a lowlevel maps
|
|
// directly to a symbol. If you are unsure if your lowlevel is an exception, assume
|
|
// that it isn't and just see if that works.
|
|
match lowlevel {
|
|
$(
|
|
LowLevel::$lowlevel => Symbol::$symbol,
|
|
)*
|
|
|
|
// these are higher-order lowlevels. these need the surrounding
|
|
// function to provide enough type information for code generation
|
|
LowLevel::ListMap => unreachable!(),
|
|
LowLevel::ListMap2 => unreachable!(),
|
|
LowLevel::ListMap3 => unreachable!(),
|
|
LowLevel::ListMap4 => unreachable!(),
|
|
LowLevel::ListSortWith => unreachable!(),
|
|
LowLevel::DictWalk => unreachable!(),
|
|
|
|
// (un)boxing is handled in a custom way
|
|
LowLevel::BoxExpr => unreachable!(),
|
|
LowLevel::UnboxExpr => unreachable!(),
|
|
|
|
// these functions return polymorphic values
|
|
LowLevel::NumIntCast => unreachable!(),
|
|
LowLevel::NumToFloatCast => unreachable!(),
|
|
LowLevel::NumToIntChecked => unreachable!(),
|
|
LowLevel::NumToFloatChecked => unreachable!(),
|
|
LowLevel::NumDivUnchecked => unreachable!(),
|
|
LowLevel::DictEmpty => unreachable!(),
|
|
|
|
// these are used internally and not tied to a symbol
|
|
LowLevel::Hash => unimplemented!(),
|
|
LowLevel::PtrCast => unimplemented!(),
|
|
LowLevel::RefCountInc => unimplemented!(),
|
|
LowLevel::RefCountDec => unimplemented!(),
|
|
|
|
// these are not implemented, not sure why
|
|
LowLevel::StrFromInt => unimplemented!(),
|
|
LowLevel::StrFromFloat => unimplemented!(),
|
|
LowLevel::NumIsFinite => unimplemented!(),
|
|
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// here is where we actually specify the mapping for the fast majority of cases that follow the
|
|
// pattern of a symbol mapping directly to a lowlevel. In other words, most lowlevels (left) are generated
|
|
// by only one specific symbol (right)
|
|
map_symbol_to_lowlevel! {
|
|
StrConcat <= STR_CONCAT,
|
|
StrJoinWith <= STR_JOIN_WITH,
|
|
StrIsEmpty <= STR_IS_EMPTY,
|
|
StrStartsWith <= STR_STARTS_WITH,
|
|
StrStartsWithScalar <= STR_STARTS_WITH_SCALAR,
|
|
StrEndsWith <= STR_ENDS_WITH,
|
|
StrSplit <= STR_SPLIT,
|
|
StrCountGraphemes <= STR_COUNT_GRAPHEMES,
|
|
StrCountUtf8Bytes <= STR_COUNT_UTF8_BYTES,
|
|
StrFromUtf8Range <= STR_FROM_UTF8_RANGE_LOWLEVEL,
|
|
StrToUtf8 <= STR_TO_UTF8,
|
|
StrRepeat <= STR_REPEAT,
|
|
StrTrim <= STR_TRIM,
|
|
StrTrimLeft <= STR_TRIM_LEFT,
|
|
StrTrimRight <= STR_TRIM_RIGHT,
|
|
StrToScalars <= STR_TO_SCALARS,
|
|
StrGetUnsafe <= STR_GET_UNSAFE,
|
|
StrSubstringUnsafe <= STR_SUBSTRING_UNSAFE,
|
|
StrReserve <= STR_RESERVE,
|
|
StrAppendScalar <= STR_APPEND_SCALAR_UNSAFE,
|
|
StrGetScalarUnsafe <= STR_GET_SCALAR_UNSAFE,
|
|
StrToNum <= STR_TO_NUM,
|
|
StrGetCapacity <= STR_CAPACITY,
|
|
ListLen <= LIST_LEN,
|
|
ListGetCapacity <= LIST_CAPACITY,
|
|
ListWithCapacity <= LIST_WITH_CAPACITY,
|
|
ListReserve <= LIST_RESERVE,
|
|
ListIsUnique <= LIST_IS_UNIQUE,
|
|
ListAppendUnsafe <= LIST_APPEND_UNSAFE,
|
|
ListPrepend <= LIST_PREPEND,
|
|
ListGetUnsafe <= LIST_GET_UNSAFE,
|
|
ListReplaceUnsafe <= LIST_REPLACE_UNSAFE,
|
|
ListConcat <= LIST_CONCAT,
|
|
ListSublist <= LIST_SUBLIST_LOWLEVEL,
|
|
ListDropAt <= LIST_DROP_AT,
|
|
ListSwap <= LIST_SWAP,
|
|
DictSize <= DICT_LEN,
|
|
DictInsert <= DICT_INSERT,
|
|
DictRemove <= DICT_REMOVE,
|
|
DictContains <= DICT_CONTAINS,
|
|
DictGetUnsafe <= DICT_GET_LOWLEVEL,
|
|
DictKeys <= DICT_KEYS,
|
|
DictValues <= DICT_VALUES,
|
|
DictUnion <= DICT_UNION,
|
|
DictIntersection <= DICT_INTERSECTION,
|
|
DictDifference <= DICT_DIFFERENCE,
|
|
DictGetCapacity <= DICT_CAPACITY,
|
|
SetFromList <= SET_FROM_LIST,
|
|
SetToDict <= SET_TO_DICT,
|
|
SetGetCapacity <= SET_CAPACITY,
|
|
NumAdd <= NUM_ADD,
|
|
NumAddWrap <= NUM_ADD_WRAP,
|
|
NumAddChecked <= NUM_ADD_CHECKED_LOWLEVEL,
|
|
NumAddSaturated <= NUM_ADD_SATURATED,
|
|
NumSub <= NUM_SUB,
|
|
NumSubWrap <= NUM_SUB_WRAP,
|
|
NumSubChecked <= NUM_SUB_CHECKED_LOWLEVEL,
|
|
NumSubSaturated <= NUM_SUB_SATURATED,
|
|
NumMul <= NUM_MUL,
|
|
NumMulWrap <= NUM_MUL_WRAP,
|
|
NumMulSaturated <= NUM_MUL_SATURATED,
|
|
NumMulChecked <= NUM_MUL_CHECKED_LOWLEVEL,
|
|
NumGt <= NUM_GT,
|
|
NumGte <= NUM_GTE,
|
|
NumLt <= NUM_LT,
|
|
NumLte <= NUM_LTE,
|
|
NumCompare <= NUM_COMPARE,
|
|
NumDivCeilUnchecked <= NUM_DIV_CEIL,
|
|
NumRemUnchecked <= NUM_REM,
|
|
NumIsMultipleOf <= NUM_IS_MULTIPLE_OF,
|
|
NumAbs <= NUM_ABS,
|
|
NumNeg <= NUM_NEG,
|
|
NumSin <= NUM_SIN,
|
|
NumCos <= NUM_COS,
|
|
NumSqrtUnchecked <= NUM_SQRT,
|
|
NumLogUnchecked <= NUM_LOG,
|
|
NumRound <= NUM_ROUND,
|
|
NumToFrac <= NUM_TO_FRAC,
|
|
NumPow <= NUM_POW,
|
|
NumCeiling <= NUM_CEILING,
|
|
NumPowInt <= NUM_POW_INT,
|
|
NumFloor <= NUM_FLOOR,
|
|
NumAtan <= NUM_ATAN,
|
|
NumAcos <= NUM_ACOS,
|
|
NumAsin <= NUM_ASIN,
|
|
NumBytesToU16 <= NUM_BYTES_TO_U16_LOWLEVEL,
|
|
NumBytesToU32 <= NUM_BYTES_TO_U32_LOWLEVEL,
|
|
NumBitwiseAnd <= NUM_BITWISE_AND,
|
|
NumBitwiseXor <= NUM_BITWISE_XOR,
|
|
NumBitwiseOr <= NUM_BITWISE_OR,
|
|
NumShiftLeftBy <= NUM_SHIFT_LEFT,
|
|
NumShiftRightBy <= NUM_SHIFT_RIGHT,
|
|
NumShiftRightZfBy <= NUM_SHIFT_RIGHT_ZERO_FILL,
|
|
NumToStr <= NUM_TO_STR,
|
|
Eq <= BOOL_EQ,
|
|
NotEq <= BOOL_NEQ,
|
|
And <= BOOL_AND,
|
|
Or <= BOOL_OR,
|
|
Not <= BOOL_NOT,
|
|
Unreachable <= LIST_UNREACHABLE,
|
|
}
|