roc/crates/compiler/module/src/low_level.rs
2022-07-13 12:19:37 -04:00

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,
}