More refactoring of class Compiler's interface.

Change more functions used by the Compiler class to have a uniform
interface: they get passed as argument an input/output pointer to a
CompilationInfo that they mutate if they succeed, and they return a
flag telling whether they succeeded.

Also, remove some unnecessary timers.

Review URL: http://codereview.chromium.org/3561012

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5583 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 2f54abf9
......@@ -3132,9 +3132,9 @@ void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
// Build the function info and instantiate it.
Handle<SharedFunctionInfo> function_info =
Compiler::BuildFunctionInfo(node, script(), this);
// Check for stack-overflow exception.
if (HasStackOverflow()) {
Compiler::BuildFunctionInfo(node, script());
if (function_info.is_null()) {
SetStackOverflow();
ASSERT(frame_->height() == original_height);
return;
}
......
......@@ -207,9 +207,7 @@ enum NopMarkerTypes {
class CodeGenerator: public AstVisitor {
public:
// Takes a function literal, generates code for it. This function should only
// be called by compiler.cc.
static Handle<Code> MakeCode(CompilationInfo* info);
static bool MakeCode(CompilationInfo* info);
// Printing of AST, etc. as requested by flags.
static void MakeCodePrologue(CompilationInfo* info);
......
......@@ -1436,6 +1436,11 @@ class FunctionLiteral: public Expression {
bool AllowsLazyCompilation();
Handle<String> debug_name() const {
if (name_->length() > 0) return name_;
return inferred_name();
}
Handle<String> inferred_name() const { return inferred_name_; }
void set_inferred_name(Handle<String> inferred_name) {
inferred_name_ = inferred_name;
......
......@@ -206,10 +206,9 @@ Handle<Code> CodeGenerator::MakeCodeEpilogue(MacroAssembler* masm,
}
// Generate the code. Takes a function literal, generates code for it, assemble
// all the pieces into a Code object. This function is only to be called by
// the compiler.cc code.
Handle<Code> CodeGenerator::MakeCode(CompilationInfo* info) {
// Generate the code. Compile the AST and assemble all the pieces into a
// Code object.
bool CodeGenerator::MakeCode(CompilationInfo* info) {
Handle<Script> script = info->script();
if (!script->IsUndefined() && !script->source()->IsUndefined()) {
int len = String::cast(script->source())->length();
......@@ -224,12 +223,14 @@ Handle<Code> CodeGenerator::MakeCode(CompilationInfo* info) {
cgen.Generate(info);
if (cgen.HasStackOverflow()) {
ASSERT(!Top::has_pending_exception());
return Handle<Code>::null();
return false;
}
InLoopFlag in_loop = (cgen.loop_nesting() != 0) ? IN_LOOP : NOT_IN_LOOP;
InLoopFlag in_loop = info->is_in_loop() ? IN_LOOP : NOT_IN_LOOP;
Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, in_loop);
return MakeCodeEpilogue(cgen.masm(), flags, info);
Handle<Code> code = MakeCodeEpilogue(cgen.masm(), flags, info);
info->SetCode(code); // May be an empty handle.
return !code.is_null();
}
......@@ -325,9 +326,12 @@ void CodeGenerator::ProcessDeclarations(ZoneList<Declaration*>* declarations) {
}
} else {
Handle<SharedFunctionInfo> function =
Compiler::BuildFunctionInfo(node->fun(), script(), this);
Compiler::BuildFunctionInfo(node->fun(), script());
// Check for stack-overflow exception.
if (HasStackOverflow()) return;
if (function.is_null()) {
SetStackOverflow();
return;
}
array->set(j++, *function);
}
}
......
This diff is collapsed.
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
......@@ -52,12 +52,14 @@ class CompilationInfo BASE_EMBEDDED {
bool is_json() const { return (flags_ & IsJson::mask()) != 0; }
bool is_in_loop() const { return (flags_ & IsInLoop::mask()) != 0; }
FunctionLiteral* function() const { return function_; }
Scope* scope() const { return function_->scope(); }
Scope* scope() const { return scope_; }
Handle<Code> code() const { return code_; }
Handle<JSFunction> closure() const { return closure_; }
Handle<SharedFunctionInfo> shared_info() const { return shared_info_; }
Handle<Script> script() const { return script_; }
v8::Extension* extension() const { return extension_; }
ScriptDataImpl* pre_parse_data() const { return pre_parse_data_; }
Handle<Context> calling_context() const { return calling_context_; }
void MarkAsEval() {
ASSERT(!is_lazy());
......@@ -79,6 +81,11 @@ class CompilationInfo BASE_EMBEDDED {
ASSERT(function_ == NULL);
function_ = literal;
}
void SetScope(Scope* scope) {
ASSERT(scope_ == NULL);
scope_ = scope;
}
void SetCode(Handle<Code> code) { code_ = code; }
void SetExtension(v8::Extension* extension) {
ASSERT(!is_lazy());
extension_ = extension;
......@@ -87,6 +94,10 @@ class CompilationInfo BASE_EMBEDDED {
ASSERT(!is_lazy());
pre_parse_data_ = pre_parse_data;
}
void SetCallingContext(Handle<Context> context) {
ASSERT(is_eval());
calling_context_ = context;
}
private:
// Flags using template class BitField<type, start, length>. All are
......@@ -104,8 +115,13 @@ class CompilationInfo BASE_EMBEDDED {
unsigned flags_;
// Fields filled in by the compilation pipeline.
// AST filled in by the parser.
FunctionLiteral* function_;
// The scope of the function literal as a convenience. Set to indidicate
// that scopes have been analyzed.
Scope* scope_;
// The compiled code.
Handle<Code> code_;
// Possible initial inputs to the compilation process.
Handle<JSFunction> closure_;
......@@ -116,6 +132,10 @@ class CompilationInfo BASE_EMBEDDED {
v8::Extension* extension_;
ScriptDataImpl* pre_parse_data_;
// The context of the caller is needed for eval code, and will be a null
// handle otherwise.
Handle<Context> calling_context_;
DISALLOW_COPY_AND_ASSIGN(CompilationInfo);
};
......@@ -127,9 +147,9 @@ class CompilationInfo BASE_EMBEDDED {
// functions, they will be compiled and allocated as part of the compilation
// of the source code.
// Please note this interface returns shared function infos.
// This means you need to call Factory::NewFunctionFromSharedFunctionInfo
// before you have a real function with a context.
// Please note this interface returns shared function infos. This means you
// need to call Factory::NewFunctionFromSharedFunctionInfo before you have a
// real function with a context.
class Compiler : public AllStatic {
public:
......@@ -155,17 +175,14 @@ class Compiler : public AllStatic {
bool is_global,
ValidationState validation);
// Compile from function info (used for lazy compilation). Returns
// true on success and false if the compilation resulted in a stack
// overflow.
// Compile from function info (used for lazy compilation). Returns true on
// success and false if the compilation resulted in a stack overflow.
static bool CompileLazy(CompilationInfo* info);
// Compile a shared function info object (the function is possibly
// lazily compiled). Called recursively from a backend code
// generator 'caller' to build the shared function info.
// Compile a shared function info object (the function is possibly lazily
// compiled).
static Handle<SharedFunctionInfo> BuildFunctionInfo(FunctionLiteral* node,
Handle<Script> script,
AstVisitor* caller);
Handle<Script> script);
// Set the function info for a newly compiled function.
static void SetFunctionInfo(Handle<SharedFunctionInfo> function_info,
......@@ -173,23 +190,18 @@ class Compiler : public AllStatic {
bool is_toplevel,
Handle<Script> script);
#ifdef ENABLE_DEBUGGER_SUPPORT
static bool MakeCodeForLiveEdit(CompilationInfo* info);
#endif
private:
static void RecordFunctionCompilation(Logger::LogEventsAndTags tag,
Handle<String> name,
Handle<String> inferred_name,
int start_position,
Handle<Script> script,
Handle<Code> code);
CompilationInfo* info);
};
#ifdef ENABLE_DEBUGGER_SUPPORT
Handle<Code> MakeCodeForLiveEdit(CompilationInfo* info);
#endif
// During compilation we need a global list of handles to constants
// for frame elements. When the zone gets deleted, we make sure to
// clear this list of handles as well.
......
......@@ -50,12 +50,13 @@ void BitVector::Print() {
#endif
bool AssignedVariablesAnalyzer::Analyze() {
Scope* scope = fun_->scope();
bool AssignedVariablesAnalyzer::Analyze(CompilationInfo* info) {
info_ = info;
Scope* scope = info->scope();
int variables = scope->num_parameters() + scope->num_stack_slots();
if (variables == 0) return true;
av_.ExpandTo(variables);
VisitStatements(fun_->body());
VisitStatements(info->function()->body());
return !HasStackOverflow();
}
......@@ -129,7 +130,7 @@ int AssignedVariablesAnalyzer::BitIndex(Variable* var) {
if (slot->type() == Slot::PARAMETER) {
return slot->index();
} else {
return fun_->scope()->num_parameters() + slot->index();
return info_->scope()->num_parameters() + slot->index();
}
}
......
......@@ -198,8 +198,8 @@ class WorkList BASE_EMBEDDED {
// is guaranteed to be a smi.
class AssignedVariablesAnalyzer : public AstVisitor {
public:
explicit AssignedVariablesAnalyzer(FunctionLiteral* fun) : fun_(fun) { }
bool Analyze();
explicit AssignedVariablesAnalyzer() : info_(NULL) { }
bool Analyze(CompilationInfo* info);
private:
Variable* FindSmiLoopVariable(ForStatement* stmt);
......@@ -219,7 +219,7 @@ class AssignedVariablesAnalyzer : public AstVisitor {
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
FunctionLiteral* fun_;
CompilationInfo* info_;
// Accumulator for assigned variables set.
BitVector av_;
......
......@@ -277,7 +277,7 @@ void BreakableStatementChecker::VisitThisFunction(ThisFunction* expr) {
#define __ ACCESS_MASM(masm())
Handle<Code> FullCodeGenerator::MakeCode(CompilationInfo* info) {
bool FullCodeGenerator::MakeCode(CompilationInfo* info) {
Handle<Script> script = info->script();
if (!script->IsUndefined() && !script->source()->IsUndefined()) {
int len = String::cast(script->source())->length();
......@@ -291,10 +291,13 @@ Handle<Code> FullCodeGenerator::MakeCode(CompilationInfo* info) {
cgen.Generate(info);
if (cgen.HasStackOverflow()) {
ASSERT(!Top::has_pending_exception());
return Handle<Code>::null();
return false;
}
Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, NOT_IN_LOOP);
return CodeGenerator::MakeCodeEpilogue(&masm, flags, info);
Handle<Code> code = CodeGenerator::MakeCodeEpilogue(&masm, flags, info);
info->SetCode(code); // may be an empty handle.
return !code.is_null();
}
......@@ -462,9 +465,12 @@ void FullCodeGenerator::VisitDeclarations(
}
} else {
Handle<SharedFunctionInfo> function =
Compiler::BuildFunctionInfo(decl->fun(), script(), this);
Compiler::BuildFunctionInfo(decl->fun(), script());
// Check for stack-overflow exception.
if (HasStackOverflow()) return;
if (function.is_null()) {
SetStackOverflow();
return;
}
array->set(j++, *function);
}
}
......@@ -1156,8 +1162,11 @@ void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
// Build the function boilerplate and instantiate it.
Handle<SharedFunctionInfo> function_info =
Compiler::BuildFunctionInfo(expr, script(), this);
if (HasStackOverflow()) return;
Compiler::BuildFunctionInfo(expr, script());
if (function_info.is_null()) {
SetStackOverflow();
return;
}
EmitNewClosure(function_info);
}
......
......@@ -74,7 +74,7 @@ class FullCodeGenerator: public AstVisitor {
context_(NULL) {
}
static Handle<Code> MakeCode(CompilationInfo* info);
static bool MakeCode(CompilationInfo* info);
void Generate(CompilationInfo* info);
......
......@@ -191,10 +191,8 @@ void CodeGenerator::Generate(CompilationInfo* info) {
}
#endif
// New scope to get automatic timing calculation.
{ HistogramTimerScope codegen_timer(&Counters::code_generation);
{
CodeGenState state(this);
// Entry:
// Stack: receiver, arguments, return address.
// ebp: caller's frame pointer
......@@ -369,7 +367,6 @@ void CodeGenerator::Generate(CompilationInfo* info) {
// Process any deferred code using the register allocator.
if (!HasStackOverflow()) {
HistogramTimerScope deferred_timer(&Counters::deferred_code_generation);
JumpTarget::set_compiling_deferred_code(true);
ProcessDeferred();
JumpTarget::set_compiling_deferred_code(false);
......@@ -4925,9 +4922,12 @@ void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
ASSERT(!in_safe_int32_mode());
// Build the function info and instantiate it.
Handle<SharedFunctionInfo> function_info =
Compiler::BuildFunctionInfo(node, script(), this);
Compiler::BuildFunctionInfo(node, script());
// Check for stack-overflow exception.
if (HasStackOverflow()) return;
if (function_info.is_null()) {
SetStackOverflow();
return;
}
Result result = InstantiateFunction(function_info);
frame()->Push(&result);
}
......
......@@ -300,9 +300,7 @@ enum ArgumentsAllocationMode {
class CodeGenerator: public AstVisitor {
public:
// Takes a function literal, generates code for it. This function should only
// be called by compiler.cc.
static Handle<Code> MakeCode(CompilationInfo* info);
static bool MakeCode(CompilationInfo* info);
// Printing of AST, etc. as requested by flags.
static void MakeCodePrologue(CompilationInfo* info);
......
......@@ -404,18 +404,16 @@ static void CompileScriptForTracker(Handle<Script> script) {
// Build AST.
CompilationInfo info(script);
info.MarkAsGlobal();
if (!Parser::Parse(&info)) return;
// Compile the code.
LiveEditFunctionTracker tracker(info.function());
Handle<Code> code = MakeCodeForLiveEdit(&info);
// Check for stack-overflow exceptions.
if (code.is_null()) {
Top::StackOverflow();
return;
if (Parser::Parse(&info)) {
// Compile the code.
LiveEditFunctionTracker tracker(info.function());
if (Compiler::MakeCodeForLiveEdit(&info)) {
ASSERT(!info.code().is_null());
tracker.RecordRootFunctionInfo(info.code());
} else {
Top::StackOverflow();
}
}
tracker.RecordRootFunctionInfo(code);
}
......
// Copyright 2006-2009 the V8 project authors. All rights reserved.
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
......@@ -5227,6 +5227,13 @@ Object* Oddball::Initialize(const char* to_string, Object* to_number) {
}
String* SharedFunctionInfo::DebugName() {
Object* n = name();
if (!n->IsString() || String::cast(n)->length() == 0) return inferred_name();
return String::cast(n);
}
bool SharedFunctionInfo::HasSourceCode() {
return !script()->IsUndefined() &&
!reinterpret_cast<Script*>(script())->source()->IsUndefined();
......
// Copyright 2006-2009 the V8 project authors. All rights reserved.
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
......@@ -3621,6 +3621,9 @@ class SharedFunctionInfo: public HeapObject {
// properties.
DECL_ACCESSORS(inferred_name, String)
// The function's name if it is non-empty, otherwise the inferred name.
String* DebugName();
// Position of the 'function' token in the script source.
inline int function_token_position();
inline void set_function_token_position(int function_token_position);
......
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
......@@ -27,9 +27,11 @@
#include "v8.h"
#include "rewriter.h"
#include "ast.h"
#include "compiler.h"
#include "scopes.h"
#include "rewriter.h"
namespace v8 {
namespace internal {
......@@ -986,34 +988,40 @@ void Processor::VisitThisFunction(ThisFunction* node) {
}
bool Rewriter::Process(FunctionLiteral* function) {
HistogramTimerScope timer(&Counters::rewriting);
// Assumes code has been parsed and scopes hve been analyzed. Mutates the
// AST, so the AST should not continue to be used in the case of failure.
bool Rewriter::Rewrite(CompilationInfo* info) {
FunctionLiteral* function = info->function();
ASSERT(function != NULL);
Scope* scope = function->scope();
ASSERT(scope != NULL);
if (scope->is_function_scope()) return true;
ZoneList<Statement*>* body = function->body();
if (body->is_empty()) return true;
if (!body->is_empty()) {
VariableProxy* result = scope->NewTemporary(Factory::result_symbol());
Processor processor(result);
processor.Process(body);
if (processor.HasStackOverflow()) return false;
VariableProxy* result = scope->NewTemporary(Factory::result_symbol());
Processor processor(result);
processor.Process(body);
if (processor.HasStackOverflow()) return false;
if (processor.result_assigned()) body->Add(new ReturnStatement(result));
}
if (processor.result_assigned()) body->Add(new ReturnStatement(result));
return true;
}
bool Rewriter::Optimize(FunctionLiteral* function) {
ZoneList<Statement*>* body = function->body();
// Assumes code has been parsed and scopes have been analyzed. Mutates the
// AST, so the AST should not continue to be used in the case of failure.
bool Rewriter::Analyze(CompilationInfo* info) {
FunctionLiteral* function = info->function();
ASSERT(function != NULL && function->scope() != NULL);
ZoneList<Statement*>* body = function->body();
if (FLAG_optimize_ast && !body->is_empty()) {
HistogramTimerScope timer(&Counters::ast_optimization);
AstOptimizer optimizer;
optimizer.Optimize(body);
if (optimizer.HasStackOverflow()) {
return false;
}
if (optimizer.HasStackOverflow()) return false;
}
return true;
}
......
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
......@@ -31,21 +31,26 @@
namespace v8 {
namespace internal {
// Currently, the rewriter takes function literals (only top-level)
// and rewrites them to return the value of the last expression in
// them.
//
// The rewriter adds a (hidden) variable, called .result, to the
// activation, and tries to figure out where it needs to store into
// this variable. If the variable is ever used, we conclude by adding
// a return statement that returns the variable to the body of the
// given function.
class CompilationInfo;
class Rewriter {
public:
static bool Process(FunctionLiteral* function);
static bool Optimize(FunctionLiteral* function);
// Rewrite top-level code (ECMA 262 "programs") so as to conservatively
// include an assignment of the value of the last statement in the code to
// a compiler-generated temporary variable wherever needed.
//
// Assumes code has been parsed and scopes have been analyzed. Mutates the
// AST, so the AST should not continue to be used in the case of failure.
static bool Rewrite(CompilationInfo* info);
// Perform a suite of simple non-iterative analyses of the AST. Mark
// expressions that are likely smis, expressions without side effects,
// expressions whose value will be converted to Int32, and expressions in a
// context where +0 and -0 are treated the same.
//
// Assumes code has been parsed and scopes have been analyzed. Mutates the
// AST, so the AST should not continue to be used in the case of failure.
static bool Analyze(CompilationInfo* info);
};
......
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
......@@ -27,9 +27,12 @@
#include "v8.h"
#include "scopes.h"
#include "bootstrapper.h"
#include "compiler.h"
#include "prettyprinter.h"
#include "scopeinfo.h"
#include "scopes.h"
namespace v8 {
namespace internal {
......@@ -168,6 +171,25 @@ Scope::Scope(Scope* outer_scope, Type type)
}
bool Scope::Analyze(CompilationInfo* info) {
ASSERT(info->function() != NULL);
Scope* top = info->function()->scope();
while (top->outer_scope() != NULL) top = top->outer_scope();
top->AllocateVariables(info->calling_context());
#ifdef DEBUG
if (Bootstrapper::IsActive()
? FLAG_print_builtin_scopes
: FLAG_print_scopes) {
info->function()->scope()->Print();
}
#endif
info->SetScope(info->function()->scope());
return true; // Can not fail.
}
void Scope::Initialize(bool inside_with) {
// Add this scope as a new inner scope of the outer scope.
if (outer_scope_ != NULL) {
......
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
......@@ -34,6 +34,9 @@
namespace v8 {
namespace internal {
class CompilationInfo;
// A hash map to support fast variable declaration and lookup.
class VariableMap: public HashMap {
public:
......@@ -96,6 +99,11 @@ class Scope: public ZoneObject {
virtual ~Scope() { }
// Compute top scope and allocate variables. For lazy compilation the top
// scope only contains the single lazily compiled function, so this
// doesn't re-allocate variables repeatedly.
static bool Analyze(CompilationInfo* info);
// The scope name is only used for printing/debugging.
void SetScopeName(Handle<String> scope_name) { scope_name_ = scope_name; }
......
// Copyright 2007-2008 the V8 project authors. All rights reserved.
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
......@@ -45,14 +45,7 @@ namespace internal {
/* Total compilation times. */ \
HT(compile, V8.Compile) \
HT(compile_eval, V8.CompileEval) \
HT(compile_lazy, V8.CompileLazy) \
/* Individual compiler passes. */ \
HT(rewriting, V8.Rewriting) \
HT(usage_analysis, V8.UsageAnalysis) \
HT(variable_allocation, V8.VariableAllocation) \
HT(ast_optimization, V8.ASTOptimization) \
HT(code_generation, V8.CodeGeneration) \
HT(deferred_code_generation, V8.DeferredCodeGeneration)
HT(compile_lazy, V8.CompileLazy)
// WARNING: STATS_COUNTER_LIST_* is a very large macro that is causing MSVC
......
......@@ -190,10 +190,8 @@ void CodeGenerator::Generate(CompilationInfo* info) {
}
#endif
// New scope to get automatic timing calculation.
{ HistogramTimerScope codegen_timer(&Counters::code_generation);
{
CodeGenState state(this);
// Entry:
// Stack: receiver, arguments, return address.
// rbp: caller's frame pointer
......@@ -367,7 +365,6 @@ void CodeGenerator::Generate(CompilationInfo* info) {
// Process any deferred code using the register allocator.
if (!HasStackOverflow()) {
HistogramTimerScope deferred_timer(&Counters::deferred_code_generation);
JumpTarget::set_compiling_deferred_code(true);
ProcessDeferred();
JumpTarget::set_compiling_deferred_code(false);
......@@ -4276,9 +4273,12 @@ void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
// Build the function info and instantiate it.
Handle<SharedFunctionInfo> function_info =
Compiler::BuildFunctionInfo(node, script(), this);
Compiler::BuildFunctionInfo(node, script());
// Check for stack-overflow exception.
if (HasStackOverflow()) return;
if (function_info.is_null()) {
SetStackOverflow();
return;
}
InstantiateFunction(function_info);
}
......
......@@ -298,9 +298,7 @@ enum ArgumentsAllocationMode {
class CodeGenerator: public AstVisitor {
public:
// Takes a function literal, generates code for it. This function should only
// be called by compiler.cc.
static Handle<Code> MakeCode(CompilationInfo* info);
static bool MakeCode(CompilationInfo* info);
// Printing of AST, etc. as requested by flags.
static void MakeCodePrologue(CompilationInfo* info);
......
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