mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 22:09:09 +00:00

These may be reintroduced in some form later, but they don't handle endianness and it's not clear builtins are the right place for them.
347 lines
10 KiB
Rust
347 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,
|
|
StrEndsWith,
|
|
StrSplit,
|
|
StrCountUtf8Bytes,
|
|
StrFromInt,
|
|
StrFromUtf8Range,
|
|
StrToUtf8,
|
|
StrRepeat,
|
|
StrFromFloat,
|
|
StrTrim,
|
|
StrTrimStart,
|
|
StrTrimEnd,
|
|
StrToNum,
|
|
StrGetUnsafe,
|
|
StrSubstringUnsafe,
|
|
StrReserve,
|
|
StrGetCapacity,
|
|
StrWithCapacity,
|
|
StrReleaseExcessCapacity,
|
|
ListLen,
|
|
ListWithCapacity,
|
|
ListReserve,
|
|
ListReleaseExcessCapacity,
|
|
ListAppendUnsafe,
|
|
ListGetUnsafe,
|
|
ListReplaceUnsafe,
|
|
ListConcat,
|
|
ListPrepend,
|
|
ListMap,
|
|
ListMap2,
|
|
ListMap3,
|
|
ListMap4,
|
|
ListSortWith,
|
|
ListSublist,
|
|
ListDropAt,
|
|
ListSwap,
|
|
ListGetCapacity,
|
|
ListIsUnique,
|
|
ListClone,
|
|
NumAdd,
|
|
NumAddWrap,
|
|
NumAddChecked,
|
|
NumAddSaturated,
|
|
NumSub,
|
|
NumSubWrap,
|
|
NumSubChecked,
|
|
NumSubSaturated,
|
|
NumMul,
|
|
NumMulWrap,
|
|
NumMulSaturated,
|
|
NumMulChecked,
|
|
NumGt,
|
|
NumGte,
|
|
NumLt,
|
|
NumLte,
|
|
NumCompare,
|
|
NumDivFrac,
|
|
NumDivTruncUnchecked,
|
|
NumDivCeilUnchecked,
|
|
NumRemUnchecked,
|
|
NumIsMultipleOf,
|
|
NumAbs,
|
|
NumNeg,
|
|
NumSin,
|
|
NumCos,
|
|
NumTan,
|
|
NumSqrtUnchecked,
|
|
NumLogUnchecked,
|
|
NumRound,
|
|
NumToFrac,
|
|
NumPow,
|
|
NumCeiling,
|
|
NumPowInt,
|
|
NumFloor,
|
|
NumIsNan,
|
|
NumIsInfinite,
|
|
NumIsFinite,
|
|
NumAtan,
|
|
NumAcos,
|
|
NumAsin,
|
|
NumBitwiseAnd,
|
|
NumBitwiseXor,
|
|
NumBitwiseOr,
|
|
NumShiftLeftBy,
|
|
NumShiftRightBy,
|
|
NumShiftRightZfBy,
|
|
NumIntCast,
|
|
NumToFloatCast,
|
|
NumToIntChecked,
|
|
NumToFloatChecked,
|
|
NumToStr,
|
|
NumCountLeadingZeroBits,
|
|
NumCountTrailingZeroBits,
|
|
NumCountOneBits,
|
|
I128OfDec,
|
|
Eq,
|
|
NotEq,
|
|
And,
|
|
Or,
|
|
Not,
|
|
Hash,
|
|
PtrCast,
|
|
PtrStore,
|
|
PtrLoad,
|
|
PtrClearTagId,
|
|
RefCountIncRcPtr,
|
|
RefCountDecRcPtr,
|
|
RefCountIncDataPtr,
|
|
RefCountDecDataPtr,
|
|
RefCountIsUnique,
|
|
BoxExpr,
|
|
UnboxExpr,
|
|
Unreachable,
|
|
DictPseudoSeed,
|
|
SetJmp,
|
|
LongJmp,
|
|
SetLongJmpBuffer,
|
|
}
|
|
|
|
macro_rules! higher_order {
|
|
() => {
|
|
ListMap | ListMap2 | ListMap3 | ListMap4 | ListSortWith
|
|
};
|
|
}
|
|
|
|
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,
|
|
_ => 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
|
|
#[derive(PartialEq, Eq)]
|
|
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) -> &'static [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!(),
|
|
|
|
// (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!(),
|
|
|
|
|
|
// these are used internally and not tied to a symbol
|
|
LowLevel::Hash => unimplemented!(),
|
|
LowLevel::PtrCast => unimplemented!(),
|
|
LowLevel::PtrStore => unimplemented!(),
|
|
LowLevel::PtrLoad => unimplemented!(),
|
|
LowLevel::PtrClearTagId => unimplemented!(),
|
|
LowLevel::RefCountIncRcPtr => unimplemented!(),
|
|
LowLevel::RefCountDecRcPtr=> unimplemented!(),
|
|
LowLevel::RefCountIncDataPtr => unimplemented!(),
|
|
LowLevel::RefCountDecDataPtr=> unimplemented!(),
|
|
LowLevel::RefCountIsUnique => unimplemented!(),
|
|
|
|
LowLevel::SetJmp => unimplemented!(),
|
|
LowLevel::LongJmp => unimplemented!(),
|
|
LowLevel::SetLongJmpBuffer => unimplemented!(),
|
|
|
|
// these are not implemented, not sure why
|
|
LowLevel::StrFromInt => unimplemented!(),
|
|
LowLevel::StrFromFloat => 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;
|
|
StrEndsWith <= STR_ENDS_WITH;
|
|
StrSplit <= STR_SPLIT;
|
|
StrCountUtf8Bytes <= STR_COUNT_UTF8_BYTES;
|
|
StrFromUtf8Range <= STR_FROM_UTF8_RANGE_LOWLEVEL;
|
|
StrToUtf8 <= STR_TO_UTF8;
|
|
StrRepeat <= STR_REPEAT;
|
|
StrTrim <= STR_TRIM;
|
|
StrTrimStart <= STR_TRIM_START;
|
|
StrTrimEnd <= STR_TRIM_END;
|
|
StrGetUnsafe <= STR_GET_UNSAFE;
|
|
StrSubstringUnsafe <= STR_SUBSTRING_UNSAFE;
|
|
StrReserve <= STR_RESERVE;
|
|
StrToNum <= STR_TO_NUM;
|
|
StrGetCapacity <= STR_CAPACITY;
|
|
StrWithCapacity <= STR_WITH_CAPACITY;
|
|
StrReleaseExcessCapacity <= STR_RELEASE_EXCESS_CAPACITY;
|
|
ListLen <= LIST_LEN;
|
|
ListGetCapacity <= LIST_CAPACITY;
|
|
ListWithCapacity <= LIST_WITH_CAPACITY;
|
|
ListReserve <= LIST_RESERVE;
|
|
ListReleaseExcessCapacity <= LIST_RELEASE_EXCESS_CAPACITY;
|
|
ListIsUnique <= LIST_IS_UNIQUE;
|
|
ListClone <= LIST_CLONE;
|
|
ListAppendUnsafe <= LIST_APPEND_UNSAFE;
|
|
ListPrepend <= LIST_PREPEND;
|
|
ListGetUnsafe <= LIST_GET_UNSAFE, DICT_LIST_GET_UNSAFE;
|
|
ListReplaceUnsafe <= LIST_REPLACE_UNSAFE;
|
|
ListConcat <= LIST_CONCAT;
|
|
ListSublist <= LIST_SUBLIST_LOWLEVEL;
|
|
ListDropAt <= LIST_DROP_AT;
|
|
ListSwap <= LIST_SWAP;
|
|
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;
|
|
NumDivFrac <= NUM_DIV_FRAC;
|
|
NumDivCeilUnchecked <= NUM_DIV_CEIL;
|
|
NumDivTruncUnchecked <= NUM_DIV_TRUNC;
|
|
NumRemUnchecked <= NUM_REM;
|
|
NumIsMultipleOf <= NUM_IS_MULTIPLE_OF;
|
|
NumAbs <= NUM_ABS;
|
|
NumNeg <= NUM_NEG;
|
|
NumSin <= NUM_SIN;
|
|
NumCos <= NUM_COS;
|
|
NumTan <= NUM_TAN;
|
|
NumSqrtUnchecked <= NUM_SQRT;
|
|
NumLogUnchecked <= NUM_LOG;
|
|
NumRound <= NUM_ROUND;
|
|
NumToFrac <= NUM_TO_FRAC;
|
|
NumIsNan <= NUM_IS_NAN;
|
|
NumIsInfinite <= NUM_IS_INFINITE;
|
|
NumIsFinite <= NUM_IS_FINITE;
|
|
NumPow <= NUM_POW;
|
|
NumCeiling <= NUM_CEILING;
|
|
NumPowInt <= NUM_POW_INT;
|
|
NumFloor <= NUM_FLOOR;
|
|
NumAtan <= NUM_ATAN;
|
|
NumAcos <= NUM_ACOS;
|
|
NumAsin <= NUM_ASIN;
|
|
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;
|
|
NumCountLeadingZeroBits <= NUM_COUNT_LEADING_ZERO_BITS;
|
|
NumCountTrailingZeroBits <= NUM_COUNT_TRAILING_ZERO_BITS;
|
|
NumCountOneBits <= NUM_COUNT_ONE_BITS;
|
|
I128OfDec <= I128_OF_DEC;
|
|
Eq <= BOOL_STRUCTURAL_EQ;
|
|
NotEq <= BOOL_STRUCTURAL_NOT_EQ;
|
|
And <= BOOL_AND;
|
|
Or <= BOOL_OR;
|
|
Not <= BOOL_NOT;
|
|
Unreachable <= LIST_UNREACHABLE;
|
|
DictPseudoSeed <= DICT_PSEUDO_SEED;
|
|
}
|