Skip to content

Commit a7274ff

Browse files
alexmalyshevmeta-codesync[bot]
authored andcommitted
Separate out jit::hir::Function into its own library
Summary: Trying to modularize Jit/hir/hir.h and keep definitions separate. Now that BasicBlock has no knowledge of CFG, the cycle from Instr to Function is broken. The new dependency chart looks like: Instr <-> BasicBlock <- CFG <-> Function. Reviewed By: DinoV, yoney Differential Revision: D88279071 fbshipit-source-id: 640199469949bbda934ea233fd55871eefd501c4
1 parent f4422ef commit a7274ff

13 files changed

Lines changed: 242 additions & 217 deletions

File tree

cinderx/Jit/codegen/frame_asm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "cinderx/Jit/codegen/arch.h"
66
#include "cinderx/Jit/codegen/environ.h"
77
#include "cinderx/Jit/codegen/register_preserver.h"
8+
#include "cinderx/Jit/hir/function.h"
89
#include "cinderx/Jit/hir/hir.h"
910
#include "cinderx/Jit/runtime.h"
1011

cinderx/Jit/compiled_function.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ bool isJitCompiled(const PyFunctionObject* func);
5656
#include "cinderx/Common/util.h"
5757
#include "cinderx/Jit/code_patcher.h"
5858
#include "cinderx/Jit/code_runtime.h"
59+
#include "cinderx/Jit/hir/function.h"
5960
#include "cinderx/Jit/hir/hir.h"
6061

6162
#include <chrono>

cinderx/Jit/debug_info.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "cinderx/Jit/debug_info.h"
44

5+
#include "cinderx/Jit/hir/function.h"
56
#include "cinderx/Jit/hir/hir.h"
67

78
#include <algorithm>

cinderx/Jit/hir/analysis.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "cinderx/Jit/hir/analysis.h"
44

55
#include "cinderx/Jit/dataflow.h"
6+
#include "cinderx/Jit/hir/function.h"
67
#include "cinderx/Jit/hir/hir.h"
78
#include "cinderx/Jit/hir/printer.h"
89
#include "cinderx/StaticPython/checked_dict.h"

cinderx/Jit/hir/analysis.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "cinderx/Jit/bitvector.h"
66
#include "cinderx/Jit/dataflow.h"
7+
#include "cinderx/Jit/hir/function.h"
78
#include "cinderx/Jit/hir/hir.h"
89

910
#include <iosfwd>
@@ -13,7 +14,6 @@
1314
namespace jit::hir {
1415

1516
class BasicBlock;
16-
class Function;
1717
class Register;
1818

1919
using RegisterSet = std::unordered_set<Register*>;

cinderx/Jit/hir/function.cpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright (c) Meta Platforms, Inc. and affiliates.
2+
3+
#include "cinderx/Jit/hir/function.h"
4+
5+
namespace jit::hir {
6+
7+
// Be intentional about HIR structure sizes. There's no hard limit on what
8+
// these sizes have to be, but we should be aware when we change them.
9+
//
10+
// Ignore it for libc++ for now though, too tricky to track multiple
11+
// implementations.
12+
#ifndef _LIBCPP_VERSION
13+
static_assert(sizeof(Function) == 48 * kPointerSize);
14+
static_assert(sizeof(CFG) == 6 * kPointerSize);
15+
static_assert(sizeof(BasicBlock) == 20 * kPointerSize);
16+
static_assert(sizeof(Instr) == 6 * kPointerSize);
17+
#endif
18+
19+
Function::Function() {
20+
cfg.func = this;
21+
}
22+
23+
Function::~Function() {
24+
// Serialize as we alter ref-counts on potentially global objects.
25+
ThreadedCompileSerialize guard;
26+
code.reset();
27+
builtins.reset();
28+
globals.reset();
29+
prim_args_info.reset();
30+
}
31+
32+
void Function::setCode(BorrowedRef<PyCodeObject> code_2) {
33+
this->code.reset(code_2);
34+
uses_runtime_func = usesRuntimeFunc(code_2);
35+
frameMode = getConfig().frame_mode;
36+
}
37+
38+
std::size_t Function::CountInstrs(InstrPredicate pred) const {
39+
std::size_t result = 0;
40+
for (const auto& block : cfg.blocks) {
41+
for (const auto& instr : block) {
42+
if (pred(instr)) {
43+
result++;
44+
}
45+
}
46+
}
47+
return result;
48+
}
49+
50+
bool Function::returnsPrimitive() const {
51+
return return_type <= TPrimitive;
52+
}
53+
54+
bool Function::returnsPrimitiveDouble() const {
55+
return return_type <= TCDouble;
56+
}
57+
58+
void Function::setCompilationPhaseTimer(
59+
std::unique_ptr<CompilationPhaseTimer> cpt) {
60+
compilation_phase_timer = std::move(cpt);
61+
}
62+
63+
int Function::numArgs() const {
64+
if (code == nullptr) {
65+
// code might be null if we parsed from textual ir
66+
return 0;
67+
}
68+
return code->co_argcount + code->co_kwonlyargcount +
69+
bool(code->co_flags & CO_VARARGS) + bool(code->co_flags & CO_VARKEYWORDS);
70+
}
71+
72+
Py_ssize_t Function::numVars() const {
73+
// Code might be null if we parsed from textual HIR.
74+
return code != nullptr ? numLocalsplus(code) : 0;
75+
}
76+
77+
bool Function::canDeopt() const {
78+
for (const BasicBlock& block : cfg.blocks) {
79+
for (const Instr& instr : block) {
80+
if (instr.asDeoptBase()) {
81+
return true;
82+
}
83+
}
84+
}
85+
return false;
86+
}
87+
88+
BorrowedRef<PyCodeObject> Function::codeFor(const Instr& instr) const {
89+
if (instr.IsBeginInlinedFunction()) {
90+
auto bif = static_cast<const BeginInlinedFunction*>(&instr);
91+
return bif->func()->func_code;
92+
}
93+
if (instr.IsLoadGlobalCached()) {
94+
auto load_global = static_cast<const LoadGlobalCached*>(&instr);
95+
return load_global->code();
96+
}
97+
if (auto deopt_base = instr.asDeoptBase()) {
98+
auto fs = deopt_base->frameState();
99+
return fs != nullptr ? fs->code : nullptr;
100+
}
101+
const FrameState* fs = instr.getDominatingFrameState();
102+
return fs == nullptr ? code : fs->code;
103+
}
104+
105+
OpcodeCounts count_opcodes(const Function& func) {
106+
OpcodeCounts counts{};
107+
for (const BasicBlock& block : func.cfg.blocks) {
108+
for (const Instr& instr : block) {
109+
counts[static_cast<size_t>(instr.opcode())]++;
110+
}
111+
}
112+
return counts;
113+
}
114+
115+
} // namespace jit::hir

cinderx/Jit/hir/function.h

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright (c) Meta Platforms, Inc. and affiliates.
2+
3+
#pragma once
4+
5+
#include "cinderx/Jit/hir/hir.h"
6+
7+
namespace jit::hir {
8+
9+
class Function {
10+
public:
11+
using InlineFailureStats =
12+
UnorderedMap<InlineFailureType, UnorderedSet<std::string>>;
13+
Function();
14+
~Function();
15+
16+
ThreadedRef<PyCodeObject> code;
17+
ThreadedRef<PyDictObject> builtins;
18+
ThreadedRef<PyDictObject> globals;
19+
20+
// for primitive args only, null if there are none
21+
ThreadedRef<_PyTypedArgsInfo> prim_args_info;
22+
23+
// Fully-qualified name of the function
24+
std::string fullname;
25+
26+
// Does this function need its PyFunctionObject* at runtime?
27+
// (This is always the case in 3.12 as it is used to quickly access the
28+
// _PyInterpreterFrame)
29+
bool uses_runtime_func{
30+
#if PY_VERSION_HEX < 0x030C0000
31+
false
32+
#else
33+
true
34+
#endif
35+
};
36+
37+
// Does this function have primitive args?
38+
bool has_primitive_args{false};
39+
40+
// is the first argument a primitive?
41+
bool has_primitive_first_arg{false};
42+
43+
struct InlineFunctionStats {
44+
int num_inlined_functions{0};
45+
// map of {inline_failure_type -> function_names}
46+
InlineFailureStats failure_stats;
47+
} inline_function_stats;
48+
49+
// vector of {locals_idx, type, optional}
50+
// in argument order, may have gaps for unchecked args
51+
std::vector<TypedArgument> typed_args;
52+
53+
// Return type
54+
Type return_type{TObject};
55+
56+
FrameMode frameMode{FrameMode::kNormal};
57+
58+
CFG cfg;
59+
60+
Environment env;
61+
62+
// All the code patchers pointing to patch points in this function.
63+
//
64+
// These will be moved over to the CompiledFunction after compilation is
65+
// complete.
66+
std::vector<std::unique_ptr<CodePatcher>> code_patchers;
67+
68+
// Optional property used to track time taken for individual compilation
69+
// phases
70+
std::unique_ptr<CompilationPhaseTimer> compilation_phase_timer;
71+
72+
// Return the total number of arguments (positional + kwonly + varargs +
73+
// varkeywords)
74+
int numArgs() const;
75+
76+
// Return the number of locals + cellvars + freevars
77+
Py_ssize_t numVars() const;
78+
79+
// Set code and a number of other members that are derived from it.
80+
void setCode(BorrowedRef<PyCodeObject> code);
81+
82+
// Count the number of instructions that match the predicate
83+
std::size_t CountInstrs(InstrPredicate pred) const;
84+
85+
// Does this function return a primitive type?
86+
bool returnsPrimitive() const;
87+
88+
// Does this function return a primitive double?
89+
bool returnsPrimitiveDouble() const;
90+
91+
void setCompilationPhaseTimer(std::unique_ptr<CompilationPhaseTimer> cpt);
92+
93+
bool canDeopt() const;
94+
95+
template <typename T, typename... Args>
96+
T* allocateCodePatcher(Args&&... args) {
97+
code_patchers.emplace_back(
98+
std::make_unique<T>(std::forward<Args>(args)...));
99+
return static_cast<T*>(code_patchers.back().get());
100+
}
101+
102+
// Get the code object for the given instruction. Handles inlined functions
103+
// but assumes that inlined functions have a dominating FrameState from
104+
// BeginInlinedFunction to use. If we start optimizing that out for inlined
105+
// functions that cannot deopt, we will have to do something different.
106+
//
107+
// The instruction must be part of this function.
108+
BorrowedRef<PyCodeObject> codeFor(const Instr& instr) const;
109+
110+
private:
111+
DISALLOW_COPY_AND_ASSIGN(Function);
112+
};
113+
114+
using OpcodeCounts = std::array<int, kNumOpcodes>;
115+
OpcodeCounts count_opcodes(const Function& func);
116+
117+
} // namespace jit::hir

0 commit comments

Comments
 (0)