前言
上一节讲到继承自ScopeNode
的4种Node
:ProgramNode
、EvalNode
、FunctionNode
和ModuleProgramNode
,它们的字节码生成最终均走到了emitStatementsBytecode
方法中。
字节码指令集
在分析之前,先大概了解下字节码的长相:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21{
"section" : "Bytecodes", "emitInHFile" : true, "emitInASMFile" : true,
"macroNameComponent" : "BYTECODE", "asmPrefix" : "llint_",
"bytecodes" : [
{ "name" : "op_enter", "length" : 1 },
{ "name" : "op_get_scope", "length" : 2 },
{ "name" : "op_create_direct_arguments", "length" : 2 },
{ "name" : "op_create_scoped_arguments", "length" : 3 },
{ "name" : "op_create_cloned_arguments", "length" : 2 },
{ "name" : "op_create_this", "length" : 5 },
{ "name" : "op_to_this", "length" : 4 },
{ "name" : "op_check_tdz", "length" : 2 },
{ "name" : "op_new_object", "length" : 4 },
{ "name" : "op_new_array", "length" : 5 },
{ "name" : "op_new_array_with_size", "length" : 4 },
{ "name" : "op_new_array_buffer", "length" : 5 },
{ "name" : "op_new_regexp", "length" : 3 },
{ "name" : "op_mov", "length" : 3 },
{ "name" : "op_not", "length" : 3 },
{ "name" : "op_eq", "length" : 4 },
...
可以看到指令的名字和长度,最后生成的字节码就是这样的指令集合。在JavaScriptCore/bytecode/BytecodeList.json
可以看到完整的指令集
emitStatementsBytecode
来看下我们期待很久的emitStatementsBytecode
吧,它是ScopeNode
的一个方法1
2
3
4
5inline void ScopeNode::emitStatementsBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
if (!m_statements) return;
m_statements->emitBytecode(generator, dst);
}
m_statements
的类型为SourceElements
,定义如下面的代码所示:它是一个单向链表,里面的节点类型为StatementNode
,这意味着存储的节点可以是StatementNode
类型,也可以是StatementNode
子类型。同时也说明,ScopeNode
由一个个串起来的StatementNode
组成:1
2
3
4
5
6
7
8
9
10class SourceElements final : public ParserArenaFreeable {
public:
SourceElements();
void append(StatementNode*);
void emitBytecode(BytecodeGenerator&, RegisterID* destination);
private:
StatementNode* m_head;
StatementNode* m_tail;
};
可以看到,循环遍历该链表,对其中的每一个节点,生成其对应的字节码1
2
3
4
5
6
7
8
9
10
11inline void SourceElements::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
for (StatementNode* statement = m_head; statement; statement = statement->next())
generator.emitNodeInTailPosition(dst, statement);
}
void emitNodeInTailPosition(RegisterID* dst, StatementNode* statementNode)
{
// Node::emitCode assumes that dst, if provided, is either a local or a referenced temporary.
statementNode->emitBytecode(*this, dst);
}
这里的statementNode
的类型可以是下面Node
中的任意一种,这些Node
均继承自StatementNode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16IfElseNode
DoWhileNode
WhileNode
ForNode
SwitchNode
ClassDecNode
DeclarationStatement
ForInNode
ForOfNode
ContinueNode
BreakNode
ReturnNode
FuncDeclNode
BlockNode
ModuleDeclarationNode
...
StatementNode
StatementNode
主要是用来描述语言结构:
(1)顺序结构:ClassDeclNode
、DeclarationStateMentNode
等
(2)条件结构:IFElseNode
、SwitchNode
等
(3)循环结构:WhileNode
、ForNode
、DoWhileNode
等
接下里将挑选一些Node
作为代表来进行分析字节码的生成
回填思想
除了顺序结构,条件结构和循环结构本身不会产生具体的逻辑指令,它们主要生成一些跳转指令,来控制程序流的走向,从而达到一定的目的。然而生成一个跳转指令时,可能并不能立刻知晓跳转的目的地,所以暂时不指定该跳转指令的目标标号,等到能够确定正确的目标标号时,才去填充这些指令的目标标号,这种技术叫做回填技术。
条件结构
1 | // Returns the next available temporary register. Registers returned by |
IfElseNode
在语法分析篇中提到:语法分析在parseIfStatement
中对IfStatement
做了规范化处理,这里看下它是如何生成字节码的。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47void IfElseNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
// generator.newLabel 生成一个用于存放标号的 新的临时变量L,返回变量地址
//
RefPtr<Label> beforeThen = generator.newLabel();
RefPtr<Label> beforeElse = generator.newLabel();
RefPtr<Label> afterElse = generator.newLabel();
Label* trueTarget = beforeThen.get();
Label* falseTarget = beforeElse.get();
FallThroughMode fallThroughMode = FallThroughMeansTrue;
// 如果是break和continue
bool didFoldIfBlock = tryFoldBreakAndContinue(generator, m_ifBlock, trueTarget, fallThroughMode);
// 生成条件表达式m_condition的字节码,可能会生成类似goto的指令
// 如果m_condition为真,goto trueTarget;否则 goto falseTarget
// 然而此时trueTarget和falseTarget的值还未能明确知道,需要等到整个
// if-else模块完毕,才可能得到明确的值,这里就会用到回填技术
generator.emitNodeInConditionContext(m_condition, trueTarget, falseTarget, fallThroughMode);
// 回填beforeThen的地址,标记m_condition为真的跳转指令
// 注意:这里只是修改beforeThen的值,并没有添加任何指令
generator.emitLabel(beforeThen.get());
// 如果是正常的if-else
if (!didFoldIfBlock) {
// 生成if-block的字节码
generator.emitNodeInTailPosition(dst, m_ifBlock);
// if模块执行完毕,跳转到afterElse,也就是整个if-else模块的下一条指令
// ** 注意:这里是一条跳转指令(goto afterElse)**
if (m_elseBlock)
generator.emitJump(afterElse.get());
}
// 填写beforeElse的地址,标记m_condition为假的跳转指令,也就是else模块的第一条指令
generator.emitLabel(beforeElse.get());
// 如果有else-block,生成其字节码
if (m_elseBlock) {
generator.emitNodeInTailPosition(dst, m_elseBlock);
}
// 回填afterElse的地址,就是整个if-else模块的下一条指令
generator.emitLabel(afterElse.get());
}
看下IfElseNode
的SDT:
SwitchNode
SwitchNode
肯定都比较熟悉了,来看下它是如何生成字节码的:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20void SwitchNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
LabelScopePtr scope = generator.newLabelScope(LabelScope::Switch);
RefPtr<RegisterID> r0 = generator.emitNode(m_expr);
// 把自己作为一个scope进行push
generator.pushLexicalScope(this, BytecodeGenerator::TDZCheckOptimization::DoNotOptimize, BytecodeGenerator::NestedScopeType::IsNested);
// 生成switch{}里面的字节码
m_block->emitBytecodeForBlock(generator, r0.get(), dst);
// pop scope
generator.popLexicalScope(this);
// 回填scope的地址,因为switch中的break的跳转地址就是switch模块的下一个地址
// 这里SwitchNode已经分析完毕,所以就可以回填其地址
generator.emitLabel(scope->breakTarget());
}
switch{}中包含了若干case语句和defalut语句1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89enum SwitchType
{
SwitchNone,
SwitchImmediate,
SwitchCharacter,
SwitchString
};
void CaseBlockNode::emitBytecodeForBlock(BytecodeGenerator& generator, RegisterID* switchExpression, RegisterID* dst)
{
RefPtr<Label> defaultLabel;
Vector<RefPtr<Label>, 8> labelVector;
Vector<ExpressionNode*, 8> literalVector;
int32_t min_num = std::numeric_limits<int32_t>::max();
int32_t max_num = std::numeric_limits<int32_t>::min();
// 确认switchType
SwitchInfo::SwitchType switchType = tryTableSwitch(literalVector, min_num, max_num);
if (switchType != SwitchInfo::SwitchNone)
{
// 如果是SwitchImmediate, SwitchCharacter, SwitchString,生成对应的case跳转标签和defalut标签
for (uint32_t i = 0; i < literalVector.size(); i++)
labelVector.append(generator.newLabel());
defaultLabel = generator.newLabel();
// 生成op_switch_imm、op_switch_char、op_switch_string指令,用于比较
generator.beginSwitch(switchExpression, switchType);
}
else
{
// 如果是SwitchNone,设置各种跳转
for (ClauseListNode* list = m_list1; list; list = list->getNext())
{
RefPtr<RegisterID> clauseVal = generator.newTemporary();
// 生成常量表达式的字节码
generator.emitNode(clauseVal.get(), list->getClause()->expr());
// 比较参数和常量表达式的字节码
generator.emitBinaryOp(op_stricteq, clauseVal.get(), clauseVal.get(), switchExpression, OperandTypes());
// 生成一个标签,用于跳转
labelVector.append(generator.newLabel());
// 如果比较结果,跳转到对应的标签
generator.emitJumpIfTrue(clauseVal.get(), labelVector[labelVector.size() - 1].get());
}
// 同上
for (ClauseListNode* list = m_list2; list; list = list->getNext()) {
RefPtr<RegisterID> clauseVal = generator.newTemporary();
generator.emitNode(clauseVal.get(), list->getClause()->expr());
generator.emitBinaryOp(op_stricteq, clauseVal.get(), clauseVal.get(), switchExpression, OperandTypes());
labelVector.append(generator.newLabel());
generator.emitJumpIfTrue(clauseVal.get(), labelVector[labelVector.size() - 1].get());
}
// 为defalut标签设置跳转
defaultLabel = generator.newLabel();
generator.emitJump(defaultLabel.get());
}
size_t i = 0;
// default前面的case
for (ClauseListNode* list = m_list1; list; list = list->getNext()) {
// 回填case标签地址
generator.emitLabel(labelVector[i++].get());
// case语句生成字节码
list->getClause()->emitBytecode(generator, dst);
}
if (m_defaultClause) {
// 回填default标签地址
generator.emitLabel(defaultLabel.get());
// default语句生成字节码
m_defaultClause->emitBytecode(generator, dst);
}
// default后面的case
for (ClauseListNode* list = m_list2; list; list = list->getNext()) {
generator.emitLabel(labelVector[i++].get());
list->getClause()->emitBytecode(generator, dst);
}
// 如果没有defalut,设置其跳转地址为switch结束的地址
if (!m_defaultClause)
generator.emitLabel(defaultLabel.get());
if (switchType != SwitchInfo::SwitchNone) {
generator.endSwitch(labelVector.size(), labelVector.data(), literalVector.data(), defaultLabel.get(), min_num, max_num);
}
}
1 | swtich E |
普通情况下,可能都会像这样按照顺序翻译SwitchNode
(这种方式比较低效)
JavaScriptCore
的SwitchNode
采取了另一种翻译方式:在代码生成阶段,如果分支的个数在一个较小的范围内,那么这种条件跳转 指令序列可以被翻译成最高效的n路分支。
可以看到SwitchNone
的跳转表不同于其它的3种方式,跳转表提前了,不过这并没有什么本质上的不同
循环结构
WhileNode
1 | void WhileNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) |
来看下WhileNode
的SDT
在循环中经常会看到break和continue,来看看他们的字节码如何生成
BreakNode
1 | void BreakNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) |
ContinueNode
1 | void ContinueNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) |
可以看到这两者最后都是加了跳转指令,跳转到其所属的scope的breakTarget或continueTarget。
另外还可以看到对区域进行了pop,这也容易理解,毕竟break和continue在一定的场合下(循环结构和switch条件结构中)才能被使用,如果触发这二者,可能会跳出当前的一个switch区域或者循环区域,直接离开了当前区域。
顺序结构
不同于前两种结构,顺序结构将会明显地看到更多更详细的字节码
ClassDeclNode
类的声明节点ClassDeclNode
的字节码生成语句仅仅只有一行熟悉的代码:1
2
3
4void ClassDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
generator.emitNode(dst, m_classDeclaration);
}
其中m_classDeclaration
的类型是ClassExprNode
,它是ExpressionNode
的一个子类,该表达式的字节码生成在后文中将会作详细的分析。
ReturnNode
1 | void ReturnNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) |
1 | RegisterID* BytecodeGenerator::emitReturn(RegisterID* src) |
可以看到,在instructions
插入了一条op_ret
指令,后面的src->index()
是src
在虚拟寄存器VirtualRegister
的偏移值
LabelNode
LabelNode
的字节码比较简单,主要是生成一个跳转标签1
2
3
4
5
6void LabelNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
LabelScopePtr scope = generator.newLabelScope(LabelScope::NamedLabel, &m_name);
generator.emitNodeInTailPosition(dst, m_statement);
generator.emitLabel(scope->breakTarget());
}
FuncDeclNode
函数的声明实际上就是要进行一个函数的注册1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33void FuncDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
{
generator.hoistSloppyModeFunctionIfNecessary(metadata()->ident());
}
void BytecodeGenerator::hoistSloppyModeFunctionIfNecessary(const Identifier& functionName)
{
if (m_scopeNode->hasSloppyModeHoistedFunction(functionName.impl()))
{
Variable currentFunctionVariable = variable(functionName);
RefPtr<RegisterID> currentValue;
if (RegisterID* local = currentFunctionVariable.local())
currentValue = local;
else
{
RefPtr<RegisterID> scope = emitResolveScope(nullptr, currentFunctionVariable);
currentValue = emitGetFromScope(newTemporary(), scope.get(), currentFunctionVariable, DoNotThrowIfNotFound);
}
// 找到符号表
SymbolTableStackEntry& varScope = m_symbolTableStack[*m_varScopeSymbolTableIndex];
SymbolTable* varSymbolTable = varScope.m_symbolTable;
// 上锁
ConcurrentJITLocker locker(ConcurrentJITLocker::NoLockingNecessary);
// 寻找函数的符号项
SymbolTableEntry entry = varSymbolTable->get(locker, functionName.impl());
bool isLexicallyScoped = false;
emitPutToScope(varScope.m_scope, variableForLocalEntry(functionName, entry, varScope.m_symbolTableConstantIndex, isLexicallyScoped), currentValue.get(), DoNotThrowIfNotFound, InitializationMode::NotInitialization);
}
}