runtime/register-state.h (47 lines of code) (raw):

/* Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) */ #pragma once #include "assembler-x64.h" #include "view.h" namespace py { namespace x64 { class WARN_UNUSED VirtualRegister { public: VirtualRegister(const char* name); operator Register(); void assign(Register reg); void free(); bool isAssigned(); const char* name(); private: Register assigned_ = kNoRegister; const char* name_; }; struct RegisterAssignment { VirtualRegister* vreg; Register reg; }; static constexpr View<RegisterAssignment> kNoRegisterAssignment = View<RegisterAssignment>(nullptr, 0); /// Register state tracker. /// Tracks assignment of registers to instances of the `VirtualRegister` class. /// This helps catching problems in code generating machine code. /// /// Straight line code example: /// VirtualRegister r_value; /// reg_state->assign(&r_value, RAX); /// __ movl(r_value, Immediate(42)); /// __ pushq(r_value); /// reg_state->clobber({..., RAX, ...}); /// __ pushq(r_value); // Fails because r_value is no longer assigned. /// /// Re-use example /// VirtualRegister r_value; /// reg_state->assign(&r_value, RAX); /// __ popq(r_value) /// // ... /// VirtualRegister r_other; /// reg_state->assign(&r_other, RAX); // this resets `r_value` kNoRegister /// __ movl(r_other, Immediate(5)); /// // .. /// __ movl(..., r_value); // Fails because `r_value` is no longer assigned /// /// Control Flow /// When there are multiple jumps to a common label then there is usually also /// an expectation to what registers values are available/unavailable in a /// particular register at that label. Use `RegisterAssignment` lists for that. /// /// // In env struct: /// VirtualRegister arg0; /// VirtualRegister arg1; /// View<RegisterAssignment> function_begin_assignment; /// Label function_begin; /// /// RegisterAssignment function_begin_assignment[] = { /// {&env->arg0, RDI}, /// {&env->arg1, RSI}, /// }; /// env->function_begin_assignment = function_begin_assignment; /// /// // Reset state to expected assignment at common label. /// // Revert all register assignment; then assign RDI to arg0, RSI to arg1. /// reg_state->resetTo(env->function_begin_assignment); /// __ bind(&env->function_begin); /// __ pushq(env->arg0); /// ... /// // Check assignment before jumping to label. Fail if arg0 is not /// // assigned to RDI or arg1 not assigned to RSI. /// reg_state->check(&env->function_begin_assignment); /// __ jmp(&env->function_begin); class RegisterState { public: RegisterState(); void assign(VirtualRegister* vreg, Register reg); void free(VirtualRegister* vreg); void allocate(VirtualRegister* vreg, View<Register> candidates); void clobber(View<Register> registers); void reset(); void resetTo(View<RegisterAssignment> assignment); void check(View<RegisterAssignment> assignment); private: VirtualRegister* assignment_[kNumRegisters]; }; inline VirtualRegister::VirtualRegister(const char* name) : name_(name) {} inline VirtualRegister::operator Register() { CHECK(isAssigned(), "no register assigned to '%s'", name_); return assigned_; } inline void VirtualRegister::assign(Register reg) { assigned_ = reg; } inline void VirtualRegister::free() { assigned_ = kNoRegister; } inline bool VirtualRegister::isAssigned() { return assigned_ != kNoRegister; } inline const char* VirtualRegister::name() { return name_; } } // namespace x64 } // namespace py