summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/asmjit/src/asmjit/x86/x86builder.h
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/asmjit/src/asmjit/x86/x86builder.h')
-rw-r--r--3rdparty/asmjit/src/asmjit/x86/x86builder.h354
1 files changed, 354 insertions, 0 deletions
diff --git a/3rdparty/asmjit/src/asmjit/x86/x86builder.h b/3rdparty/asmjit/src/asmjit/x86/x86builder.h
new file mode 100644
index 00000000000..194c1402f11
--- /dev/null
+++ b/3rdparty/asmjit/src/asmjit/x86/x86builder.h
@@ -0,0 +1,354 @@
+// This file is part of AsmJit project <https://asmjit.com>
+//
+// See asmjit.h or LICENSE.md for license and copyright information
+// SPDX-License-Identifier: Zlib
+
+#ifndef ASMJIT_X86_X86BUILDER_H_INCLUDED
+#define ASMJIT_X86_X86BUILDER_H_INCLUDED
+
+#include "../core/api-config.h"
+#ifndef ASMJIT_NO_BUILDER
+
+#include "../core/builder.h"
+#include "../x86/x86emitter.h"
+
+ASMJIT_BEGIN_SUB_NAMESPACE(x86)
+
+//! \addtogroup asmjit_x86
+//! \{
+
+//! X86/X64 builder implementation.
+//!
+//! The code representation used by \ref BaseBuilder is compatible with everything AsmJit provides. Each instruction
+//! is stored as \ref InstNode, which contains instruction id, options, and operands. Each instruction emitted will
+//! create a new \ref InstNode instance and add it to the current cursor in the double-linked list of nodes. Since
+//! the instruction stream used by \ref BaseBuilder can be manipulated, we can rewrite the SumInts example from
+//! \ref asmjit_assembler into the following:
+//!
+//! ```
+//! #include <asmjit/x86.h>
+//! #include <stdio.h>
+//!
+//! using namespace asmjit;
+//!
+//! typedef void (*SumIntsFunc)(int* dst, const int* a, const int* b);
+//!
+//! // Small helper function to print the current content of `cb`.
+//! static void dumpCode(BaseBuilder& builder, const char* phase) {
+//! String sb;
+//! formatOptions formatOptions {};
+//!
+//! Formatter::formatNodeList(sb, formatOptions, &builder);
+//! printf("%s:\n%s\n", phase, sb.data());
+//! }
+//!
+//! int main() {
+//! JitRuntime rt; // Create JIT Runtime.
+//! CodeHolder code; // Create a CodeHolder.
+//!
+//! code.init(rt.environment(), // Initialize code to match the JIT environment.
+//! rt.cpuFeatures());
+//! x86::Builder cb(&code); // Create and attach x86::Builder to `code`.
+//!
+//! // Decide which registers will be mapped to function arguments. Try changing registers
+//! // of `dst`, `srcA`, and `srcB` and see what happens in function's prolog and epilog.
+//! x86::Gp dst = cb.zax();
+//! x86::Gp srcA = cb.zcx();
+//! x86::Gp srcB = cb.zdx();
+//!
+//! X86::Xmm vec0 = x86::xmm0;
+//! X86::Xmm vec1 = x86::xmm1;
+//!
+//! // Create and initialize `FuncDetail`.
+//! FuncDetail func;
+//! func.init(FuncSignature::build<void, int*, const int*, const int*>());
+//!
+//! // Remember prolog insertion point.
+//! BaseNode* prologInsertionPoint = cb.cursor();
+//!
+//! // Emit function body:
+//! cb.movdqu(vec0, x86::ptr(srcA)); // Load 4 ints from [srcA] to XMM0.
+//! cb.movdqu(vec1, x86::ptr(srcB)); // Load 4 ints from [srcB] to XMM1.
+//! cb.paddd(vec0, vec1); // Add 4 ints in XMM1 to XMM0.
+//! cb.movdqu(x86::ptr(dst), vec0); // Store the result to [dst].
+//!
+//! // Remember epilog insertion point.
+//! BaseNode* epilogInsertionPoint = cb.cursor();
+//!
+//! // Let's see what we have now.
+//! dumpCode(cb, "Raw Function");
+//!
+//! // Now, after we emitted the function body, we can insert the prolog, arguments
+//! // allocation, and epilog. This is not possible with using pure x86::Assembler.
+//! FuncFrame frame;
+//! frame.init(func);
+//!
+//! // Make XMM0 and XMM1 dirty; RegGroup::kVec describes XMM|YMM|ZMM registers.
+//! frame.setDirtyRegs(RegGroup::kVec, IntUtils::mask(0, 1));
+//!
+//! FuncArgsAssignment args(&func); // Create arguments assignment context.
+//! args.assignAll(dst, srcA, srcB); // Assign our registers to arguments.
+//! args.updateFrame(frame); // Reflect our args in FuncFrame.
+//! frame.finalize(); // Finalize the FuncFrame (updates it).
+//!
+//! // Insert function prolog and allocate arguments to registers.
+//! cb.setCursor(prologInsertionPoint);
+//! cb.emitProlog(frame);
+//! cb.emitArgsAssignment(frame, args);
+//!
+//! // Insert function epilog.
+//! cb.setCursor(epilogInsertionPoint);
+//! cb.emitEpilog(frame);
+//!
+//! // Let's see how the function's prolog and epilog looks.
+//! dumpCode(cb, "Prolog & Epilog");
+//!
+//! // IMPORTANT: Builder requires finalize() to be called to serialize its
+//! // code to the Assembler (it automatically creates one if not attached).
+//! cb.finalize();
+//!
+//! SumIntsFunc fn;
+//! Error err = rt.add(&fn, &code); // Add the generated code to the runtime.
+//! if (err) return 1; // Handle a possible error case.
+//!
+//! // Execute the generated function.
+//! int inA[4] = { 4, 3, 2, 1 };
+//! int inB[4] = { 1, 5, 2, 8 };
+//! int out[4];
+//! fn(out, inA, inB);
+//!
+//! // Prints {5 8 4 9}
+//! printf("{%d %d %d %d}\n", out[0], out[1], out[2], out[3]);
+//!
+//! rt.release(fn); // Explicitly remove the function from the runtime.
+//! return 0;
+//! }
+//! ```
+//!
+//! When the example is executed it should output the following (this one using AMD64-SystemV ABI):
+//!
+//! ```
+//! Raw Function:
+//! movdqu xmm0, [rcx]
+//! movdqu xmm1, [rdx]
+//! paddd xmm0, xmm1
+//! movdqu [rax], xmm0
+//!
+//! Prolog & Epilog:
+//! mov rax, rdi
+//! mov rcx, rsi
+//! movdqu xmm0, [rcx]
+//! movdqu xmm1, [rdx]
+//! paddd xmm0, xmm1
+//! movdqu [rax], xmm0
+//! ret
+//!
+//! {5 8 4 9}
+//! ```
+//!
+//! The number of use-cases of \ref BaseBuilder is not limited and highly depends on your creativity and experience.
+//! The previous example can be easily improved to collect all dirty registers inside the function programmatically
+//! and to pass them to \ref FuncFrame::setDirtyRegs().
+//!
+//! ```
+//! #include <asmjit/x86.h>
+//!
+//! using namespace asmjit;
+//!
+//! // NOTE: This function doesn't cover all possible constructs. It ignores instructions that write
+//! // to implicit registers that are not part of the operand list. It also counts read-only registers.
+//! // Real implementation would be a bit more complicated, but still relatively easy to implement.
+//! static void collectDirtyRegs(const BaseNode* first,
+//! const BaseNode* last,
+//! Support::Array<RegMask, Globals::kNumVirtGroups>& regMask) {
+//! const BaseNode* node = first;
+//! while (node) {
+//! if (node->actsAsInst()) {
+//! const InstNode* inst = node->as<InstNode>();
+//! const Operand* opArray = inst->operands();
+//!
+//! for (uint32_t i = 0, opCount = inst->opCount(); i < opCount; i++) {
+//! const Operand& op = opArray[i];
+//! if (op.isReg()) {
+//! const x86::Reg& reg = op.as<x86::Reg>();
+//! if (reg.group() <= RegGroup::kMaxVirt) {
+//! regMask[reg.group()] |= 1u << reg.id();
+//! }
+//! }
+//! }
+//! }
+//!
+//! if (node == last)
+//! break;
+//! node = node->next();
+//! }
+//! }
+//!
+//! static void setDirtyRegsOfFuncFrame(const x86::Builder& builder, FuncFrame& frame) {
+//! Support::Array<RegMask, Globals::kNumVirtGroups> regMask {};
+//! collectDirtyRegs(builder.firstNode(), builder.lastNode(), regMask);
+//!
+//! // X86/X64 ABIs only require to save GP/XMM registers:
+//! frame.setDirtyRegs(RegGroup::kGp, regMask[RegGroup::kGp]);
+//! frame.setDirtyRegs(RegGroup::kVec, regMask[RegGroup::kVec]);
+//! }
+//! ```
+//!
+//! ### Casting Between Various Emitters
+//!
+//! Even when \ref BaseAssembler and \ref BaseBuilder provide the same interface as defined by \ref BaseEmitter their
+//! platform dependent variants like \ref x86::Assembler and \ref x86::Builder cannot be interchanged or casted to each
+//! other by using a C++ `static_cast<>`. The main reason is the inheritance graph of these classes is different and
+//! cast-incompatible, as illustrated below:
+//!
+//! ```
+//! +--------------+ +=========================+
+//! +----------------------->| x86::Emitter |<--+--# x86::EmitterImplicitT<> #<--+
+//! | +--------------+ | +=========================+ |
+//! | (abstract) | (mixin) |
+//! | +--------------+ +~~~~~~~~~~~~~~+ | |
+//! +-->| BaseAssembler|---->|x86::Assembler|<--+ |
+//! | +--------------+ +~~~~~~~~~~~~~~+ | |
+//! | (abstract) (final) | |
+//! +===============+ | +--------------+ +~~~~~~~~~~~~~~+ | |
+//! # BaseEmitter #--+-->| BaseBuilder |--+->| x86::Builder |<--+ |
+//! +===============+ +--------------+ | +~~~~~~~~~~~~~~+ |
+//! (abstract) (abstract) | (final) |
+//! +---------------------+ |
+//! | |
+//! | +--------------+ +~~~~~~~~~~~~~~+ +=========================+ |
+//! +-->| BaseCompiler |---->| x86::Compiler|<-----# x86::EmitterExplicitT<> #---+
+//! +--------------+ +~~~~~~~~~~~~~~+ +=========================+
+//! (abstract) (final) (mixin)
+//! ```
+//!
+//! The graph basically shows that it's not possible to cast between \ref x86::Assembler and \ref x86::Builder.
+//! However, since both share the base interface (\ref BaseEmitter) it's possible to cast them to a class that
+//! cannot be instantiated, but defines the same interface - the class is called \ref x86::Emitter and was
+//! introduced to make it possible to write a function that can emit to both \ref x86::Assembler and \ref
+//! x86::Builder. Note that \ref x86::Emitter cannot be created, it's abstract and has private constructors and
+//! destructors; it was only designed to be casted to and used as an interface.
+//!
+//! Each architecture-specific emitter implements a member function called
+//! `as<arch::Emitter>()`, which casts the instance to the architecture
+//! specific emitter as illustrated below:
+//!
+//! ```
+//! #include <asmjit/x86.h>
+//!
+//! using namespace asmjit;
+//!
+//! static void emitSomething(x86::Emitter* e) {
+//! e->mov(x86::eax, x86::ebx);
+//! }
+//!
+//! static void assemble(CodeHolder& code, bool useAsm) {
+//! if (useAsm) {
+//! x86::Assembler assembler(&code);
+//! emitSomething(assembler.as<x86::Emitter>());
+//! }
+//! else {
+//! x86::Builder builder(&code);
+//! emitSomething(builder.as<x86::Emitter>());
+//!
+//! // NOTE: Builder requires `finalize()` to be called to serialize its
+//! // content to Assembler (it automatically creates one if not attached).
+//! builder.finalize();
+//! }
+//! }
+//! ```
+//!
+//! The example above shows how to create a function that can emit code to either \ref x86::Assembler or \ref
+//! x86::Builder through \ref x86::Emitter, which provides emitter-neutral functionality. \ref x86::Emitter,
+//! however, doesn't provide any emitter-specific functionality like `setCursor()`.
+//!
+//! ### Code Injection and Manipulation
+//!
+//! \ref BaseBuilder emitter stores its nodes in a double-linked list, which makes it easy to manipulate that
+//! list during the code generation or afterwards. Each node is always emitted next to the current cursor and
+//! the cursor is advanced to that newly emitted node. The cursor can be retrieved and changed by \ref
+//! BaseBuilder::cursor() and \ref BaseBuilder::setCursor(), respectively.
+//!
+//! The example below demonstrates how to remember a node and inject something
+//! next to it.
+//!
+//! ```
+//! static void example(x86::Builder& builder) {
+//! // Emit something, after it returns the cursor would point at the last
+//! // emitted node.
+//! builder.mov(x86::rax, x86::rdx); // [1]
+//!
+//! // We can retrieve the node.
+//! BaseNode* node = builder.cursor();
+//!
+//! // Change the instruction we just emitted, just for fun...
+//! if (node->isInst()) {
+//! InstNode* inst = node->as<InstNode>();
+//! // Changes the operands at index [1] to RCX.
+//! inst->setOp(1, x86::rcx);
+//! }
+//!
+//! // ------------------------- Generate Some Code -------------------------
+//! builder.add(x86::rax, x86::rdx); // [2]
+//! builder.shr(x86::rax, 3); // [3]
+//! // ----------------------------------------------------------------------
+//!
+//! // Now, we know where our node is, and we can simply change the cursor
+//! // and start emitting something after it. The setCursor() function
+//! // returns the previous cursor, and it's always a good practice to remember
+//! // it, because you never know if you are not already injecting the code
+//! // somewhere else...
+//! BaseNode* oldCursor = builder.setCursor(node);
+//!
+//! builder.mul(x86::rax, 8); // [4]
+//!
+//! // Restore the cursor
+//! builder.setCursor(oldCursor);
+//! }
+//! ```
+//!
+//! The function above would actually emit the following:
+//!
+//! ```
+//! mov rax, rcx ; [1] Patched at the beginning.
+//! mul rax, 8 ; [4] Injected.
+//! add rax, rdx ; [2] Followed [1] initially.
+//! shr rax, 3 ; [3] Follows [2].
+//! ```
+class ASMJIT_VIRTAPI Builder
+ : public BaseBuilder,
+ public EmitterImplicitT<Builder> {
+public:
+ ASMJIT_NONCOPYABLE(Builder)
+ typedef BaseBuilder Base;
+
+ //! \name Construction & Destruction
+ //! \{
+
+ ASMJIT_API explicit Builder(CodeHolder* code = nullptr) noexcept;
+ ASMJIT_API ~Builder() noexcept override;
+
+ //! \}
+
+ //! \name Events
+ //! \{
+
+ ASMJIT_API Error onAttach(CodeHolder* code) noexcept override;
+ ASMJIT_API Error onDetach(CodeHolder* code) noexcept override;
+
+ //! \}
+
+ //! \name Finalize
+ //! \{
+
+ ASMJIT_API Error finalize() override;
+
+ //! \}
+};
+
+//! \}
+
+ASMJIT_END_SUB_NAMESPACE
+
+#endif // !ASMJIT_NO_BUILDER
+#endif // ASMJIT_X86_X86BUILDER_H_INCLUDED