JavaScriptCore引擎深度解析5-字节码生成篇(中)

前言

上一节讲到继承自ScopeNode的4种NodeProgramNodeEvalNodeFunctionNodeModuleProgramNode,它们的字节码生成最终均走到了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
5
inline 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
10
class 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
11
inline 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
16
IfElseNode
DoWhileNode
WhileNode
ForNode
SwitchNode
ClassDecNode
DeclarationStatement
ForInNode
ForOfNode
ContinueNode
BreakNode
ReturnNode
FuncDeclNode
BlockNode
ModuleDeclarationNode
...

StatementNode


StatementNode主要是用来描述语言结构:
(1)顺序结构:ClassDeclNodeDeclarationStateMentNode
(2)条件结构:IFElseNodeSwitchNode
(3)循环结构:WhileNodeForNodeDoWhileNode

接下里将挑选一些Node作为代表来进行分析字节码的生成

回填思想
除了顺序结构,条件结构和循环结构本身不会产生具体的逻辑指令,它们主要生成一些跳转指令,来控制程序流的走向,从而达到一定的目的。然而生成一个跳转指令时,可能并不能立刻知晓跳转的目的地,所以暂时不指定该跳转指令的目标标号,等到能够确定正确的目标标号时,才去填充这些指令的目标标号,这种技术叫做回填技术。

条件结构

1
2
3
4
// Returns the next available temporary register. Registers returned by
// newTemporary require a modified form of reference counting: any
// register with a refcount of 0 is considered "available", meaning that
// the next instruction may overwrite it.

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
47
void 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
20
void 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
89
enum 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
2
3
4
5
6
7
8
swtich E
begin
case V1 : S1
case V2: S2
...
case Vn-1 : Sn-1
default : Sn
End

普通情况下,可能都会像这样按照顺序翻译SwitchNode(这种方式比较低效)

JavaScriptCoreSwitchNode采取了另一种翻译方式:在代码生成阶段,如果分支的个数在一个较小的范围内,那么这种条件跳转 指令序列可以被翻译成最高效的n路分支。

可以看到SwitchNone的跳转表不同于其它的3种方式,跳转表提前了,不过这并没有什么本质上的不同

循环结构

WhileNode

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
void WhileNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
// 生成一个scope标签,用于continue和break
LabelScopePtr scope = generator.newLabelScope(LabelScope::Loop);

// 生成循环体的起始地址标签
RefPtr<Label> topOfLoop = generator.newLabel();

// 生成条件表达式的字节码,如果为真,跳转到topOfLoop,否则跳转到breakTarget
generator.emitNodeInConditionContext(m_expr, topOfLoop.get(), scope->breakTarget(), FallThroughMeansTrue);

// 回填起始地址
generator.emitLabel(topOfLoop.get());

// 插入op_loop_hint循环指令
generator.emitLoopHint();

// 生成循环体的字节码
generator.emitNodeInTailPosition(dst, m_statement);

// 回填continueTarget的地址
generator.emitLabel(scope->continueTarget());

// 生成条件表达式的字节码,如果为真,跳转到topOfLoop,否则跳转到breakTarget
generator.emitNodeInConditionContext(m_expr, topOfLoop.get(), scope->breakTarget(), FallThroughMeansFalse);

// 回填breakTarget的地址
generator.emitLabel(scope->breakTarget());

}

来看下WhileNode的SDT

在循环中经常会看到break和continue,来看看他们的字节码如何生成

BreakNode

1
2
3
4
5
6
void BreakNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
{
LabelScopePtr scope = generator.breakTarget(m_ident);
generator.emitPopScopes(generator.scopeRegister(), scope->scopeDepth());
generator.emitJump(scope->breakTarget());
}

ContinueNode

1
2
3
4
5
6
void ContinueNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
{
LabelScopePtr scope = generator.continueTarget(m_ident);
generator.emitPopScopes(generator.scopeRegister(), scope->scopeDepth());
generator.emitJump(scope->continueTarget());
}

可以看到这两者最后都是加了跳转指令,跳转到其所属的scope的breakTarget或continueTarget。
另外还可以看到对区域进行了pop,这也容易理解,毕竟break和continue在一定的场合下(循环结构和switch条件结构中)才能被使用,如果触发这二者,可能会跳出当前的一个switch区域或者循环区域,直接离开了当前区域。

顺序结构

不同于前两种结构,顺序结构将会明显地看到更多更详细的字节码

ClassDeclNode

类的声明节点ClassDeclNode的字节码生成语句仅仅只有一行熟悉的代码:

1
2
3
4
void ClassDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
generator.emitNode(dst, m_classDeclaration);
}

其中m_classDeclaration的类型是ClassExprNode,它是ExpressionNode的一个子类,该表达式的字节码生成在后文中将会作详细的分析。

ReturnNode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void ReturnNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
// 方法忽略返回值
if (dst == generator.ignoredResult()) dst = 0;

// 生成return后的表达式的字节码
RefPtr<RegisterID> returnRegister = m_value ?
generator.emitNodeInTailPosition(dst, m_value) :
generator.emitLoad(dst, jsUndefined());

...
//
generator.emitReturn(returnRegister.get());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
RegisterID* BytecodeGenerator::emitReturn(RegisterID* src)
{
// 实际上这里还有对构造函数的特殊处理
...

// 生成一元操作符字节码
return emitUnaryNoDstOp(op_ret, src);
}
RegisterID* BytecodeGenerator::emitUnaryNoDstOp(OpcodeID opcodeID, RegisterID* src)
{
emitOpcode(opcodeID);
instructions().append(src->index());
return src;
}

可以看到,在instructions插入了一条op_ret指令,后面的src->index()src在虚拟寄存器VirtualRegister的偏移值

LabelNode

LabelNode的字节码比较简单,主要是生成一个跳转标签

1
2
3
4
5
6
void 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
33
void 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);
}
}

未完待续

-------------本文结束 感谢您的阅读-------------

本文标题:JavaScriptCore引擎深度解析5-字节码生成篇(中)

文章作者:lingyun

发布时间:2018年08月15日 - 00:08

最后更新:2019年04月25日 - 23:04

原始链接:https://tsuijunxi.github.io/2018/08/15/JavaScriptCore引擎深度解析-5-字节码生成篇(中)/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!

本文标题:JavaScriptCore引擎深度解析5-字节码生成篇(中)

文章作者:lingyun

发布时间:2018年08月15日 - 00:08

最后更新:2019年04月25日 - 23:04

原始链接:https://tsuijunxi.github.io/2018/08/15/JavaScriptCore引擎深度解析-5-字节码生成篇(中)/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。