Commit 5b4fa790 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[turbolizer] Highlight gap instructions in the disassembly view

Change-Id: I9988ea2dfeccbfaa9e0197920703ab430a43acb7
Bug: v8:7327
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1674026
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarDaniel Clifford <danno@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63375}
parent 0736599a
...@@ -148,7 +148,7 @@ void CodeGenerator::AssembleCode() { ...@@ -148,7 +148,7 @@ void CodeGenerator::AssembleCode() {
if (info->is_source_positions_enabled()) { if (info->is_source_positions_enabled()) {
AssembleSourcePosition(start_source_position()); AssembleSourcePosition(start_source_position());
} }
offsets_info_.code_start_register_check = tasm()->pc_offset();
// Check that {kJavaScriptCallCodeStartRegister} has been set correctly. // Check that {kJavaScriptCallCodeStartRegister} has been set correctly.
if (FLAG_debug_code && (info->code_kind() == Code::OPTIMIZED_FUNCTION || if (FLAG_debug_code && (info->code_kind() == Code::OPTIMIZED_FUNCTION ||
info->code_kind() == Code::BYTECODE_HANDLER)) { info->code_kind() == Code::BYTECODE_HANDLER)) {
...@@ -156,6 +156,7 @@ void CodeGenerator::AssembleCode() { ...@@ -156,6 +156,7 @@ void CodeGenerator::AssembleCode() {
AssembleCodeStartRegisterCheck(); AssembleCodeStartRegisterCheck();
} }
offsets_info_.deopt_check = tasm()->pc_offset();
// We want to bailout only from JS functions, which are the only ones // We want to bailout only from JS functions, which are the only ones
// that are optimized. // that are optimized.
if (info->IsOptimizing()) { if (info->IsOptimizing()) {
...@@ -164,6 +165,7 @@ void CodeGenerator::AssembleCode() { ...@@ -164,6 +165,7 @@ void CodeGenerator::AssembleCode() {
BailoutIfDeoptimized(); BailoutIfDeoptimized();
} }
offsets_info_.init_poison = tasm()->pc_offset();
InitializeSpeculationPoison(); InitializeSpeculationPoison();
// Define deoptimization literals for all inlined functions. // Define deoptimization literals for all inlined functions.
...@@ -193,10 +195,10 @@ void CodeGenerator::AssembleCode() { ...@@ -193,10 +195,10 @@ void CodeGenerator::AssembleCode() {
if (info->trace_turbo_json_enabled()) { if (info->trace_turbo_json_enabled()) {
block_starts_.assign(instructions()->instruction_blocks().size(), -1); block_starts_.assign(instructions()->instruction_blocks().size(), -1);
instr_starts_.assign(instructions()->instructions().size(), -1); instr_starts_.assign(instructions()->instructions().size(), {});
} }
// Assemble instructions in assembly order. // Assemble instructions in assembly order.
offsets_info_.blocks_start = tasm()->pc_offset();
for (const InstructionBlock* block : instructions()->ao_blocks()) { for (const InstructionBlock* block : instructions()->ao_blocks()) {
// Align loop headers on vendor recommended boundaries. // Align loop headers on vendor recommended boundaries.
if (block->ShouldAlign() && !tasm()->jump_optimization_info()) { if (block->ShouldAlign() && !tasm()->jump_optimization_info()) {
...@@ -254,6 +256,7 @@ void CodeGenerator::AssembleCode() { ...@@ -254,6 +256,7 @@ void CodeGenerator::AssembleCode() {
} }
// Assemble all out-of-line code. // Assemble all out-of-line code.
offsets_info_.out_of_line_code = tasm()->pc_offset();
if (ools_) { if (ools_) {
tasm()->RecordComment("-- Out of line code --"); tasm()->RecordComment("-- Out of line code --");
for (OutOfLineCode* ool = ools_; ool; ool = ool->next()) { for (OutOfLineCode* ool = ools_; ool; ool = ool->next()) {
...@@ -277,6 +280,7 @@ void CodeGenerator::AssembleCode() { ...@@ -277,6 +280,7 @@ void CodeGenerator::AssembleCode() {
} }
// Assemble deoptimization exits. // Assemble deoptimization exits.
offsets_info_.deoptimization_exits = tasm()->pc_offset();
int last_updated = 0; int last_updated = 0;
for (DeoptimizationExit* exit : deoptimization_exits_) { for (DeoptimizationExit* exit : deoptimization_exits_) {
if (exit->emitted()) continue; if (exit->emitted()) continue;
...@@ -298,12 +302,14 @@ void CodeGenerator::AssembleCode() { ...@@ -298,12 +302,14 @@ void CodeGenerator::AssembleCode() {
if (result_ != kSuccess) return; if (result_ != kSuccess) return;
} }
offsets_info_.pools = tasm()->pc_offset();
// TODO(jgruber): Move all inlined metadata generation into a new, // TODO(jgruber): Move all inlined metadata generation into a new,
// architecture-independent version of FinishCode. Currently, this includes // architecture-independent version of FinishCode. Currently, this includes
// the safepoint table, handler table, constant pool, and code comments, in // the safepoint table, handler table, constant pool, and code comments, in
// that order. // that order.
FinishCode(); FinishCode();
offsets_info_.jump_tables = tasm()->pc_offset();
// Emit the jump tables. // Emit the jump tables.
if (jump_tables_) { if (jump_tables_) {
tasm()->Align(kSystemPointerSize); tasm()->Align(kSystemPointerSize);
...@@ -489,11 +495,7 @@ bool CodeGenerator::IsMaterializableFromRoot(Handle<HeapObject> object, ...@@ -489,11 +495,7 @@ bool CodeGenerator::IsMaterializableFromRoot(Handle<HeapObject> object,
CodeGenerator::CodeGenResult CodeGenerator::AssembleBlock( CodeGenerator::CodeGenResult CodeGenerator::AssembleBlock(
const InstructionBlock* block) { const InstructionBlock* block) {
for (int i = block->code_start(); i < block->code_end(); ++i) { for (int i = block->code_start(); i < block->code_end(); ++i) {
if (info()->trace_turbo_json_enabled()) { CodeGenResult result = AssembleInstruction(i, block);
instr_starts_[i] = tasm()->pc_offset();
}
Instruction* instr = instructions()->InstructionAt(i);
CodeGenResult result = AssembleInstruction(instr, block);
if (result != kSuccess) return result; if (result != kSuccess) return result;
} }
return kSuccess; return kSuccess;
...@@ -647,7 +649,11 @@ RpoNumber CodeGenerator::ComputeBranchInfo(BranchInfo* branch, ...@@ -647,7 +649,11 @@ RpoNumber CodeGenerator::ComputeBranchInfo(BranchInfo* branch,
} }
CodeGenerator::CodeGenResult CodeGenerator::AssembleInstruction( CodeGenerator::CodeGenResult CodeGenerator::AssembleInstruction(
Instruction* instr, const InstructionBlock* block) { int instruction_index, const InstructionBlock* block) {
Instruction* instr = instructions()->InstructionAt(instruction_index);
if (info()->trace_turbo_json_enabled()) {
instr_starts_[instruction_index].gap_pc_offset = tasm()->pc_offset();
}
int first_unused_stack_slot; int first_unused_stack_slot;
FlagsMode mode = FlagsModeField::decode(instr->opcode()); FlagsMode mode = FlagsModeField::decode(instr->opcode());
if (mode != kFlags_trap) { if (mode != kFlags_trap) {
...@@ -665,10 +671,17 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleInstruction( ...@@ -665,10 +671,17 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleInstruction(
if (instr->IsJump() && block->must_deconstruct_frame()) { if (instr->IsJump() && block->must_deconstruct_frame()) {
AssembleDeconstructFrame(); AssembleDeconstructFrame();
} }
if (info()->trace_turbo_json_enabled()) {
instr_starts_[instruction_index].arch_instr_pc_offset = tasm()->pc_offset();
}
// Assemble architecture-specific code for the instruction. // Assemble architecture-specific code for the instruction.
CodeGenResult result = AssembleArchInstruction(instr); CodeGenResult result = AssembleArchInstruction(instr);
if (result != kSuccess) return result; if (result != kSuccess) return result;
if (info()->trace_turbo_json_enabled()) {
instr_starts_[instruction_index].condition_pc_offset = tasm()->pc_offset();
}
FlagsCondition condition = FlagsConditionField::decode(instr->opcode()); FlagsCondition condition = FlagsConditionField::decode(instr->opcode());
switch (mode) { switch (mode) {
case kFlags_branch: case kFlags_branch:
......
...@@ -85,6 +85,25 @@ class DeoptimizationLiteral { ...@@ -85,6 +85,25 @@ class DeoptimizationLiteral {
const StringConstantBase* string_ = nullptr; const StringConstantBase* string_ = nullptr;
}; };
// These structs hold pc offsets for generated instructions and is only used
// when tracing for turbolizer is enabled.
struct TurbolizerCodeOffsetsInfo {
int code_start_register_check = -1;
int deopt_check = -1;
int init_poison = -1;
int blocks_start = -1;
int out_of_line_code = -1;
int deoptimization_exits = -1;
int pools = -1;
int jump_tables = -1;
};
struct TurbolizerInstructionStartInfo {
int gap_pc_offset = -1;
int arch_instr_pc_offset = -1;
int condition_pc_offset = -1;
};
// Generates native code for a sequence of instructions. // Generates native code for a sequence of instructions.
class V8_EXPORT_PRIVATE CodeGenerator final : public GapResolver::Assembler { class V8_EXPORT_PRIVATE CodeGenerator final : public GapResolver::Assembler {
public: public:
...@@ -139,7 +158,13 @@ class V8_EXPORT_PRIVATE CodeGenerator final : public GapResolver::Assembler { ...@@ -139,7 +158,13 @@ class V8_EXPORT_PRIVATE CodeGenerator final : public GapResolver::Assembler {
size_t GetHandlerTableOffset() const { return handler_table_offset_; } size_t GetHandlerTableOffset() const { return handler_table_offset_; }
const ZoneVector<int>& block_starts() const { return block_starts_; } const ZoneVector<int>& block_starts() const { return block_starts_; }
const ZoneVector<int>& instr_starts() const { return instr_starts_; } const ZoneVector<TurbolizerInstructionStartInfo>& instr_starts() const {
return instr_starts_;
}
const TurbolizerCodeOffsetsInfo& offsets_info() const {
return offsets_info_;
}
static constexpr int kBinarySearchSwitchMinimalCases = 4; static constexpr int kBinarySearchSwitchMinimalCases = 4;
...@@ -182,7 +207,7 @@ class V8_EXPORT_PRIVATE CodeGenerator final : public GapResolver::Assembler { ...@@ -182,7 +207,7 @@ class V8_EXPORT_PRIVATE CodeGenerator final : public GapResolver::Assembler {
void GenerateSpeculationPoisonFromCodeStartRegister(); void GenerateSpeculationPoisonFromCodeStartRegister();
// Assemble code for the specified instruction. // Assemble code for the specified instruction.
CodeGenResult AssembleInstruction(Instruction* instr, CodeGenResult AssembleInstruction(int instruction_index,
const InstructionBlock* block); const InstructionBlock* block);
void AssembleGaps(Instruction* instr); void AssembleGaps(Instruction* instr);
...@@ -419,7 +444,8 @@ class V8_EXPORT_PRIVATE CodeGenerator final : public GapResolver::Assembler { ...@@ -419,7 +444,8 @@ class V8_EXPORT_PRIVATE CodeGenerator final : public GapResolver::Assembler {
CodeGenResult result_; CodeGenResult result_;
PoisoningMitigationLevel poisoning_level_; PoisoningMitigationLevel poisoning_level_;
ZoneVector<int> block_starts_; ZoneVector<int> block_starts_;
ZoneVector<int> instr_starts_; TurbolizerCodeOffsetsInfo offsets_info_;
ZoneVector<TurbolizerInstructionStartInfo> instr_starts_;
}; };
} // namespace compiler } // namespace compiler
......
...@@ -2921,7 +2921,7 @@ void PipelineImpl::VerifyGeneratedCodeIsIdempotent() { ...@@ -2921,7 +2921,7 @@ void PipelineImpl::VerifyGeneratedCodeIsIdempotent() {
} }
struct InstructionStartsAsJSON { struct InstructionStartsAsJSON {
const ZoneVector<int>* instr_starts; const ZoneVector<TurbolizerInstructionStartInfo>* instr_starts;
}; };
std::ostream& operator<<(std::ostream& out, const InstructionStartsAsJSON& s) { std::ostream& operator<<(std::ostream& out, const InstructionStartsAsJSON& s) {
...@@ -2929,14 +2929,39 @@ std::ostream& operator<<(std::ostream& out, const InstructionStartsAsJSON& s) { ...@@ -2929,14 +2929,39 @@ std::ostream& operator<<(std::ostream& out, const InstructionStartsAsJSON& s) {
bool need_comma = false; bool need_comma = false;
for (size_t i = 0; i < s.instr_starts->size(); ++i) { for (size_t i = 0; i < s.instr_starts->size(); ++i) {
if (need_comma) out << ", "; if (need_comma) out << ", ";
int offset = (*s.instr_starts)[i]; const TurbolizerInstructionStartInfo& info = (*s.instr_starts)[i];
out << "\"" << i << "\":" << offset; out << "\"" << i << "\": {";
out << "\"gap\": " << info.gap_pc_offset;
out << ", \"arch\": " << info.arch_instr_pc_offset;
out << ", \"condition\": " << info.condition_pc_offset;
out << "}";
need_comma = true; need_comma = true;
} }
out << "}"; out << "}";
return out; return out;
} }
struct TurbolizerCodeOffsetsInfoAsJSON {
const TurbolizerCodeOffsetsInfo* offsets_info;
};
std::ostream& operator<<(std::ostream& out,
const TurbolizerCodeOffsetsInfoAsJSON& s) {
out << ", \"codeOffsetsInfo\": {";
out << "\"codeStartRegisterCheck\": "
<< s.offsets_info->code_start_register_check << ", ";
out << "\"deoptCheck\": " << s.offsets_info->deopt_check << ", ";
out << "\"initPoison\": " << s.offsets_info->init_poison << ", ";
out << "\"blocksStart\": " << s.offsets_info->blocks_start << ", ";
out << "\"outOfLineCode\": " << s.offsets_info->out_of_line_code << ", ";
out << "\"deoptimizationExits\": " << s.offsets_info->deoptimization_exits
<< ", ";
out << "\"pools\": " << s.offsets_info->pools << ", ";
out << "\"jumpTables\": " << s.offsets_info->jump_tables;
out << "}";
return out;
}
void PipelineImpl::AssembleCode(Linkage* linkage, void PipelineImpl::AssembleCode(Linkage* linkage,
std::unique_ptr<AssemblerBuffer> buffer) { std::unique_ptr<AssemblerBuffer> buffer) {
PipelineData* data = this->data_; PipelineData* data = this->data_;
...@@ -2948,7 +2973,9 @@ void PipelineImpl::AssembleCode(Linkage* linkage, ...@@ -2948,7 +2973,9 @@ void PipelineImpl::AssembleCode(Linkage* linkage,
TurboJsonFile json_of(data->info(), std::ios_base::app); TurboJsonFile json_of(data->info(), std::ios_base::app);
json_of << "{\"name\":\"code generation\"" json_of << "{\"name\":\"code generation\""
<< ", \"type\":\"instructions\"" << ", \"type\":\"instructions\""
<< InstructionStartsAsJSON{&data->code_generator()->instr_starts()}; << InstructionStartsAsJSON{&data->code_generator()->instr_starts()}
<< TurbolizerCodeOffsetsInfoAsJSON{
&data->code_generator()->offsets_info()};
json_of << "},\n"; json_of << "},\n";
} }
data->DeleteInstructionZone(); data->DeleteInstructionZone();
......
...@@ -13,6 +13,7 @@ const toolboxHTML = `<div id="disassembly-toolbox"> ...@@ -13,6 +13,7 @@ const toolboxHTML = `<div id="disassembly-toolbox">
<form> <form>
<label><input id="show-instruction-address" type="checkbox" name="instruction-address">Show addresses</label> <label><input id="show-instruction-address" type="checkbox" name="instruction-address">Show addresses</label>
<label><input id="show-instruction-binary" type="checkbox" name="instruction-binary">Show binary literal</label> <label><input id="show-instruction-binary" type="checkbox" name="instruction-binary">Show binary literal</label>
<label><input id="highlight-gap-instructions" type="checkbox" name="instruction-binary">Highlight gap instructions</label>
</form> </form>
</div>`; </div>`;
...@@ -26,6 +27,7 @@ export class DisassemblyView extends TextView { ...@@ -26,6 +27,7 @@ export class DisassemblyView extends TextView {
offsetSelection: MySelection; offsetSelection: MySelection;
showInstructionAddressHandler: () => void; showInstructionAddressHandler: () => void;
showInstructionBinaryHandler: () => void; showInstructionBinaryHandler: () => void;
highlightGapInstructionsHandler: () => void;
createViewElement() { createViewElement() {
const pane = document.createElement('div'); const pane = document.createElement('div');
...@@ -46,6 +48,9 @@ export class DisassemblyView extends TextView { ...@@ -46,6 +48,9 @@ export class DisassemblyView extends TextView {
associateData: (text, fragment: HTMLElement) => { associateData: (text, fragment: HTMLElement) => {
const matches = text.match(/(?<address>0?x?[0-9a-fA-F]{8,16})(?<addressSpace>\s+)(?<offset>[0-9a-f]+)(?<offsetSpace>\s*)/); const matches = text.match(/(?<address>0?x?[0-9a-fA-F]{8,16})(?<addressSpace>\s+)(?<offset>[0-9a-f]+)(?<offsetSpace>\s*)/);
const offset = Number.parseInt(matches.groups["offset"], 16); const offset = Number.parseInt(matches.groups["offset"], 16);
const instructionKind = view.sourceResolver.getInstructionKindForPCOffset(offset);
fragment.dataset.instructionKind = instructionKind;
fragment.title = view.sourceResolver.instructionKindToReadableName(instructionKind);
const blockIds = view.sourceResolver.getBlockIdsForOffset(offset); const blockIds = view.sourceResolver.getBlockIdsForOffset(offset);
const blockIdElement = document.createElement("SPAN"); const blockIdElement = document.createElement("SPAN");
blockIdElement.className = "block-id com linkable-text"; blockIdElement.className = "block-id com linkable-text";
...@@ -242,6 +247,17 @@ export class DisassemblyView extends TextView { ...@@ -242,6 +247,17 @@ export class DisassemblyView extends TextView {
}; };
instructionBinaryInput.addEventListener("change", showInstructionBinaryHandler); instructionBinaryInput.addEventListener("change", showInstructionBinaryHandler);
this.showInstructionBinaryHandler = showInstructionBinaryHandler; this.showInstructionBinaryHandler = showInstructionBinaryHandler;
const highlightGapInstructionsInput: HTMLInputElement = view.divNode.querySelector("#highlight-gap-instructions");
const lastHighlightGapInstructions = window.sessionStorage.getItem("highlight-gap-instructions");
highlightGapInstructionsInput.checked = lastHighlightGapInstructions == 'true';
const highlightGapInstructionsHandler = () => {
window.sessionStorage.setItem("highlight-gap-instructions", `${highlightGapInstructionsInput.checked}`);
view.divNode.classList.toggle("highlight-gap-instructions", highlightGapInstructionsInput.checked);
};
highlightGapInstructionsInput.addEventListener("change", highlightGapInstructionsHandler);
this.highlightGapInstructionsHandler = highlightGapInstructionsHandler;
} }
updateSelection(scrollIntoView: boolean = false) { updateSelection(scrollIntoView: boolean = false) {
...@@ -308,6 +324,7 @@ export class DisassemblyView extends TextView { ...@@ -308,6 +324,7 @@ export class DisassemblyView extends TextView {
super.initializeContent(data, null); super.initializeContent(data, null);
this.showInstructionAddressHandler(); this.showInstructionAddressHandler();
this.showInstructionBinaryHandler(); this.showInstructionBinaryHandler();
this.highlightGapInstructionsHandler();
console.timeEnd("disassembly-view"); console.timeEnd("disassembly-view");
} }
......
...@@ -98,8 +98,10 @@ export class SequenceView extends TextView { ...@@ -98,8 +98,10 @@ export class SequenceView extends TextView {
const instNodeEl = createElement("div", "instruction-node"); const instNodeEl = createElement("div", "instruction-node");
const instId = createElement("div", "instruction-id", instruction.id); const instId = createElement("div", "instruction-id", instruction.id);
const offsets = view.sourceResolver.instructionToPcOffsets(instruction.id);
instId.classList.add("clickable"); instId.classList.add("clickable");
instId.dataset.instructionId = instruction.id; instId.dataset.instructionId = instruction.id;
instId.setAttribute("title", `This instruction generated gap code at pc-offset 0x${offsets.gap.toString(16)}, code at pc-offset 0x${offsets.arch.toString(16)}, condition handling at pc-offset 0x${offsets.condition.toString(16)}.`);
instNodeEl.appendChild(instId); instNodeEl.appendChild(instId);
const instContentsEl = createElement("div", "instruction-contents"); const instContentsEl = createElement("div", "instruction-contents");
......
...@@ -83,6 +83,7 @@ interface InstructionsPhase { ...@@ -83,6 +83,7 @@ interface InstructionsPhase {
instructionOffsetToPCOffset?: any; instructionOffsetToPCOffset?: any;
blockIdtoInstructionRange?: any; blockIdtoInstructionRange?: any;
nodeIdToInstructionRange?: any; nodeIdToInstructionRange?: any;
codeOffsetsInfo?: CodeOffsetsInfo
} }
interface GraphPhase { interface GraphPhase {
...@@ -103,6 +104,22 @@ export interface Sequence { ...@@ -103,6 +104,22 @@ export interface Sequence {
blocks: Array<any>; blocks: Array<any>;
} }
class CodeOffsetsInfo {
codeStartRegisterCheck: number;
deoptCheck: number;
initPoison: number;
blocksStart: number;
outOfLineCode: number;
deoptimizationExits: number;
pools: number;
jumpTables: number;
}
export class TurbolizerInstructionStartInfo {
gap: number;
arch: number;
condition: number;
}
export class SourceResolver { export class SourceResolver {
nodePositionMap: Array<AnyPosition>; nodePositionMap: Array<AnyPosition>;
sources: Array<Source>; sources: Array<Source>;
...@@ -115,11 +132,12 @@ export class SourceResolver { ...@@ -115,11 +132,12 @@ export class SourceResolver {
lineToSourcePositions: Map<string, Array<AnyPosition>>; lineToSourcePositions: Map<string, Array<AnyPosition>>;
nodeIdToInstructionRange: Array<[number, number]>; nodeIdToInstructionRange: Array<[number, number]>;
blockIdToInstructionRange: Array<[number, number]>; blockIdToInstructionRange: Array<[number, number]>;
instructionToPCOffset: Array<number>; instructionToPCOffset: Array<TurbolizerInstructionStartInfo>;
pcOffsetToInstructions: Map<number, Array<number>>; pcOffsetToInstructions: Map<number, Array<number>>;
pcOffsets: Array<number>; pcOffsets: Array<number>;
blockIdToPCOffset: Array<number>; blockIdToPCOffset: Array<number>;
blockStartPCtoBlockIds: Map<number, Array<number>>; blockStartPCtoBlockIds: Map<number, Array<number>>;
codeOffsetsInfo: CodeOffsetsInfo;
constructor() { constructor() {
// Maps node ids to source positions. // Maps node ids to source positions.
...@@ -151,6 +169,7 @@ export class SourceResolver { ...@@ -151,6 +169,7 @@ export class SourceResolver {
this.pcOffsets = []; this.pcOffsets = [];
this.blockIdToPCOffset = []; this.blockIdToPCOffset = [];
this.blockStartPCtoBlockIds = new Map(); this.blockStartPCtoBlockIds = new Map();
this.codeOffsetsInfo = null;
} }
getBlockIdsForOffset(offset): Array<number> { getBlockIdsForOffset(offset): Array<number> {
...@@ -381,12 +400,18 @@ export class SourceResolver { ...@@ -381,12 +400,18 @@ export class SourceResolver {
} }
readInstructionOffsetToPCOffset(instructionToPCOffset) { readInstructionOffsetToPCOffset(instructionToPCOffset) {
for (const [instruction, offset] of Object.entries<number>(instructionToPCOffset)) { for (const [instruction, numberOrInfo] of Object.entries<number | TurbolizerInstructionStartInfo>(instructionToPCOffset)) {
this.instructionToPCOffset[instruction] = offset; let info: TurbolizerInstructionStartInfo;
if (!this.pcOffsetToInstructions.has(offset)) { if (typeof numberOrInfo == "number") {
this.pcOffsetToInstructions.set(offset, []); info = { gap: numberOrInfo, arch: numberOrInfo, condition: numberOrInfo };
} else {
info = numberOrInfo;
} }
this.pcOffsetToInstructions.get(offset).push(Number(instruction)); this.instructionToPCOffset[instruction] = info;
if (!this.pcOffsetToInstructions.has(info.gap)) {
this.pcOffsetToInstructions.set(info.gap, []);
}
this.pcOffsetToInstructions.get(info.gap).push(Number(instruction));
} }
this.pcOffsets = Array.from(this.pcOffsetToInstructions.keys()).sort((a, b) => b - a); this.pcOffsets = Array.from(this.pcOffsetToInstructions.keys()).sort((a, b) => b - a);
} }
...@@ -405,15 +430,67 @@ export class SourceResolver { ...@@ -405,15 +430,67 @@ export class SourceResolver {
return -1; return -1;
} }
instructionRangeToKeyPcOffsets([start, end]: [number, number]) { getInstructionKindForPCOffset(offset: number) {
if (this.codeOffsetsInfo) {
if (offset >= this.codeOffsetsInfo.deoptimizationExits) {
if (offset >= this.codeOffsetsInfo.pools) {
return "pools";
} else if (offset >= this.codeOffsetsInfo.jumpTables) {
return "jump-tables";
} else {
return "deoptimization-exits";
}
}
if (offset < this.codeOffsetsInfo.deoptCheck) {
return "code-start-register";
} else if (offset < this.codeOffsetsInfo.initPoison) {
return "deopt-check";
} else if (offset < this.codeOffsetsInfo.blocksStart) {
return "init-poison";
}
}
const keyOffset = this.getKeyPcOffset(offset);
if (keyOffset != -1) {
const infos = this.pcOffsetToInstructions.get(keyOffset).map(instrId => this.instructionToPCOffset[instrId]).filter(info => info.gap != info.condition);
if (infos.length > 0) {
const info = infos[0];
if (!info || info.gap == info.condition) return "unknown";
if (offset < info.arch) return "gap";
if (offset < info.condition) return "arch";
return "condition";
}
}
return "unknown";
}
instructionKindToReadableName(instructionKind) {
switch (instructionKind) {
case "code-start-register": return "Check code register for right value";
case "deopt-check": return "Check if function was marked for deoptimization";
case "init-poison": return "Initialization of poison register";
case "gap": return "Instruction implementing a gap move";
case "arch": return "Instruction implementing the actual machine operation";
case "condition": return "Code implementing conditional after instruction";
case "pools": return "Data in a pool (e.g. constant pool)";
case "jump-tables": return "Part of a jump table";
case "deoptimization-exits": return "Jump to deoptimization exit";
}
return null;
}
instructionRangeToKeyPcOffsets([start, end]: [number, number]): Array<TurbolizerInstructionStartInfo> {
if (start == end) return [this.instructionToPCOffset[start]]; if (start == end) return [this.instructionToPCOffset[start]];
return this.instructionToPCOffset.slice(start, end); return this.instructionToPCOffset.slice(start, end);
} }
instructionsToKeyPcOffsets(instructionIds: Iterable<number>) { instructionToPcOffsets(instr: number): TurbolizerInstructionStartInfo {
return this.instructionToPCOffset[instr];
}
instructionsToKeyPcOffsets(instructionIds: Iterable<number>): Array<number> {
const keyPcOffsets = []; const keyPcOffsets = [];
for (const instructionId of instructionIds) { for (const instructionId of instructionIds) {
keyPcOffsets.push(this.instructionToPCOffset[instructionId]); keyPcOffsets.push(this.instructionToPCOffset[instructionId].gap);
} }
return keyPcOffsets; return keyPcOffsets;
} }
...@@ -487,6 +564,9 @@ export class SourceResolver { ...@@ -487,6 +564,9 @@ export class SourceResolver {
if (phase.instructionOffsetToPCOffset) { if (phase.instructionOffsetToPCOffset) {
this.readInstructionOffsetToPCOffset(phase.instructionOffsetToPCOffset); this.readInstructionOffsetToPCOffset(phase.instructionOffsetToPCOffset);
} }
if (phase.codeOffsetsInfo) {
this.codeOffsetsInfo = phase.codeOffsetsInfo;
}
break; break;
case 'graph': case 'graph':
const graphPhase: GraphPhase = Object.assign(phase, { highestNodeId: 0 }); const graphPhase: GraphPhase = Object.assign(phase, { highestNodeId: 0 });
......
...@@ -702,3 +702,55 @@ ul.disassembly-list .block-id { ...@@ -702,3 +702,55 @@ ul.disassembly-list .block-id {
display: block; display: block;
padding-top: 2px; padding-top: 2px;
} }
div.highlight-gap-instructions [data-instruction-kind="gap"]+span+span {
background-color: #FAEEEE;
}
div.highlight-gap-instructions [data-instruction-kind="arch"]+span+span {
background-color: #EEFFEE;
}
div.highlight-gap-instructions [data-instruction-kind="condition"]+span+span {
background-color: #FFFFEE;
}
div.highlight-gap-instructions [data-instruction-kind="gap"] {
background-color: #FAEEEE;
}
div.highlight-gap-instructions [data-instruction-kind="arch"] {
background-color: #EEFFEE;
}
div.highlight-gap-instructions [data-instruction-kind="condition"] {
background-color: #FFFFEE;
}
div.highlight-gap-instructions [data-instruction-kind="deopt-check"] {
background-color: #FAEEFA;
}
div.highlight-gap-instructions [data-instruction-kind="init-poison"] {
background-color: #EEFFAA;
}
div.highlight-gap-instructions [data-instruction-kind="pools"] {
background-color: #6AA84F;
}
div.highlight-gap-instructions [data-instruction-kind="code-start-register"] {
background-color: #FFCCCC;
}
div.highlight-gap-instructions [data-instruction-kind="deoptimization-exits"] {
background-color: #CCCCFF;
}
[data-instruction-kind].selected {
background-color: yellow;
}
div.highlight-gap-instructions [data-instruction-kind].selected {
background-color: yellow;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment