Use a custom generic tagged union Either instead of std::variant for efficiency (#1476)

* Implement custom generic tagged union `Either`

This should be more efficient than `std::variant`, while still
keeping runtime safety as it `assert`s when `get`ting values.

* Use `Either` for RPN expressions

* Use `Either` for file stack node data

* Use `Either` for `File` buffer

* Use `Either` for `STRFMT` args

* Use `Either` for RGBLINK symbol values

* Support an equivalent of `std::monostate` for `Either`

* Use `Either` for lexer tokens

* Use `Either` for symbol values

* Use `Either` for lexer mmap/buffer state
This commit is contained in:
Sylvie
2024-08-20 15:19:11 -04:00
committed by GitHub
parent 7d98b9a900
commit 57c3d74b9e
17 changed files with 303 additions and 174 deletions

View File

@@ -10,16 +10,16 @@
#include <stdint.h>
#include <stdio.h>
#include <string>
#include <variant>
#include <vector>
#include "either.hpp"
#include "linkdefs.hpp"
#include "asm/lexer.hpp"
struct FileStackNode {
FileStackNodeType type;
std::variant<
Either<
std::vector<uint32_t>, // NODE_REPT
std::string // NODE_FILE, NODE_MACRO
>
@@ -34,13 +34,13 @@ struct FileStackNode {
uint32_t ID = -1;
// REPT iteration counts since last named node, in reverse depth order
std::vector<uint32_t> &iters();
std::vector<uint32_t> const &iters() const;
std::vector<uint32_t> &iters() { return data.get<std::vector<uint32_t>>(); }
std::vector<uint32_t> const &iters() const { return data.get<std::vector<uint32_t>>(); }
// File name for files, file::macro name for macros
std::string &name();
std::string const &name() const;
std::string &name() { return data.get<std::string>(); }
std::string const &name() const { return data.get<std::string>(); }
FileStackNode(FileStackNodeType type_, std::variant<std::vector<uint32_t>, std::string> data_)
FileStackNode(FileStackNodeType type_, Either<std::vector<uint32_t>, std::string> data_)
: type(type_), data(data_){};
std::string const &dump(uint32_t curLineNo) const;

View File

@@ -8,9 +8,9 @@
#include <optional>
#include <stdint.h>
#include <string>
#include <variant>
#include <vector>
#include "either.hpp"
#include "platform.hpp" // SSIZE_MAX
// This value is a compromise between `LexerState` allocation performance when `mmap` works, and
@@ -98,7 +98,7 @@ struct LexerState {
bool expandStrings;
std::deque<Expansion> expansions; // Front is the innermost current expansion
std::variant<std::monostate, ViewedContent, BufferedContent> content;
Either<ViewedContent, BufferedContent> content;
~LexerState();

View File

@@ -5,18 +5,19 @@
#include <stdint.h>
#include <string>
#include <variant>
#include <vector>
#include "either.hpp"
#include "linkdefs.hpp"
struct Symbol;
struct Expression {
std::variant<
int32_t, // If the expression's value is known, it's here
std::string // Why the expression is not known, if it isn't
> data = 0;
Either<
int32_t, // If the expression's value is known, it's here
std::string // Why the expression is not known, if it isn't
>
data = 0;
bool isSymbol = false; // Whether the expression represents a symbol suitable for const diffing
std::vector<uint8_t> rpn{}; // Bytes serializing the RPN expression
uint32_t rpnPatchSize = 0; // Size the expression will take in the object file
@@ -30,8 +31,12 @@ struct Expression {
Expression &operator=(Expression &&) = default;
bool isKnown() const { return std::holds_alternative<int32_t>(data); }
int32_t value() const;
bool isKnown() const {
return data.holds<int32_t>();
}
int32_t value() const {
return data.get<int32_t>();
}
int32_t getConstVal() const;
Symbol const *symbolOf() const;