[builtins] Emit builtins as inline assembly on windows clang builds

We recently changed embedded builtins to be emitted as raw assembly
files during the build process in order to support MSVC (which doesn't
support inline assembly on x64). Ninja uses ml.exe / ml64.exe as the
assembler on all Windows builds (msvc & clang); these unfortunately
don't support large data streams well and can take over 5 minutes for
embedded.S.

With this CL we work around this by going back to inlined assembly for
clang Windows builds.

Bug: v8:6666, v8:8475
Change-Id: I33beb3f5a1df07de3299df0fc2be4e8983701db0
Reviewed-on: https://chromium-review.googlesource.com/c/1344114
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Sergiy Belozorov <sergiyb@chromium.org>
Reviewed-by: Michael Stanton <mvstanton@chromium.org>
Reviewed-by: Michael Achenbach <machenbach@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57726}
This commit is contained in:
Jakob Gruber 2018-11-22 11:25:50 +01:00 committed by Commit Bot
parent b0b5eae096
commit 8c1248932e
4 changed files with 167 additions and 72 deletions

View File

@ -24,6 +24,9 @@ import("snapshot_toolchain.gni")
is_target_simulator = (target_cpu != v8_target_cpu && !v8_multi_arch_build) ||
(current_cpu != v8_current_cpu && v8_multi_arch_build)
# For faster Windows builds. See https://crbug.com/v8/8475.
emit_builtins_as_inline_asm = is_win && is_clang
declare_args() {
# Print to stdout on Android.
v8_android_log_stdout = false
@ -398,10 +401,7 @@ config("features") {
defines += [ "V8_CHECK_MICROTASKS_SCOPES_CONSISTENCY" ]
}
if (v8_enable_embedded_builtins) {
defines += [
"V8_EMBEDDED_BUILTINS",
"V8_EMBEDDED_BYTECODE_HANDLERS",
]
defines += [ "V8_EMBEDDED_BUILTINS" ]
}
if (v8_enable_jitless_mode) {
defines += [ "V8_JITLESS_MODE" ]
@ -680,6 +680,39 @@ config("v8_gcov_coverage_ldflags") {
# Actions
#
# Only for Windows clang builds. Converts the embedded.S file produced by
# mksnapshot into an embedded.cc file with corresponding inline assembly.
template("asm_to_inline_asm") {
name = target_name
if (name == "default") {
suffix = ""
} else {
suffix = "_$name"
}
action("asm_to_inline_asm_" + name) {
visibility = [ ":*" ] # Only targets in this file can depend on this.
assert(emit_builtins_as_inline_asm)
script = "tools/snapshot/asm_to_inline_asm.py"
deps = [
":run_mksnapshot_" + name,
]
sources = [
"$target_gen_dir/embedded${suffix}.S",
]
outputs = [
"$target_gen_dir/embedded${suffix}.cc",
]
args = invoker.args
args += [
rebase_path("$target_gen_dir/embedded${suffix}.S", root_build_dir),
rebase_path("$target_gen_dir/embedded${suffix}.cc", root_build_dir),
]
}
}
action("js2c") {
visibility = [ ":*" ] # Only targets in this file can depend on this.
@ -1133,6 +1166,11 @@ if (v8_use_snapshot) {
embedded_variant = "Default"
}
}
if (emit_builtins_as_inline_asm) {
asm_to_inline_asm("default") {
args = []
}
}
if (v8_use_multi_snapshots) {
run_mksnapshot("trusted") {
args = [ "--no-untrusted-code-mitigations" ]
@ -1140,6 +1178,11 @@ if (v8_use_snapshot) {
embedded_variant = "Trusted"
}
}
if (emit_builtins_as_inline_asm) {
asm_to_inline_asm("trusted") {
args = []
}
}
}
}
@ -1267,7 +1310,10 @@ if (v8_use_snapshot && !v8_use_external_startup_data) {
"src/setup-isolate-deserialize.cc",
]
if (v8_enable_embedded_builtins) {
if (v8_enable_embedded_builtins && emit_builtins_as_inline_asm) {
deps += [ ":asm_to_inline_asm_default" ]
sources += [ "$target_gen_dir/embedded.cc" ]
} else if (v8_enable_embedded_builtins) {
sources += [ "$target_gen_dir/embedded.S" ]
} else {
sources += [ "src/snapshot/embedded-empty.cc" ]
@ -1314,7 +1360,19 @@ if (v8_use_snapshot && v8_use_external_startup_data) {
# Do not publicize any header to remove build dependency.
public = []
if (v8_enable_embedded_builtins) {
if (v8_enable_embedded_builtins && emit_builtins_as_inline_asm) {
deps += [ ":asm_to_inline_asm_default" ]
sources += [ "$target_gen_dir/embedded.cc" ]
if (v8_use_multi_snapshots) {
deps += [ ":asm_to_inline_asm_trusted" ]
sources += [ "$target_gen_dir/embedded_trusted.cc" ]
if (use_jumbo_build == true) {
jumbo_excluded_sources = [ "$target_gen_dir/embedded_trusted.cc" ]
}
}
} else if (v8_enable_embedded_builtins) {
sources += [ "$target_gen_dir/embedded.S" ]
if (v8_use_multi_snapshots) {

View File

@ -9,6 +9,23 @@
namespace v8 {
namespace internal {
// V8_CC_MSVC is true for both MSVC and clang on windows. clang can handle
// __asm__-style inline assembly but MSVC cannot, and thus we need a more
// precise compiler detection that can distinguish between the two. clang on
// windows sets both __clang__ and _MSC_VER, MSVC sets only _MSC_VER.
#if defined(_MSC_VER) && !defined(__clang__)
#define V8_COMPILER_IS_MSVC
#endif
// Name mangling.
// Symbols are prefixed with an underscore on 32-bit architectures.
#if defined(V8_OS_WIN) && !defined(V8_TARGET_ARCH_X64) && \
!defined(V8_TARGET_ARCH_ARM64)
#define SYMBOL_PREFIX "_"
#else
#define SYMBOL_PREFIX ""
#endif
// Platform-independent bits.
// -----------------------------------------------------------------------------
@ -25,10 +42,19 @@ DataDirective PointerSizeDirective() {
} // namespace
// static
const char* PlatformDependentEmbeddedFileWriter::DirectiveAsString(
DataDirective directive) {
#ifndef V8_OS_WIN
const char* DirectiveAsString(DataDirective directive) {
#if defined(V8_OS_WIN) && defined(V8_COMPILER_IS_MSVC)
switch (directive) {
case kByte:
return "BYTE";
case kLong:
return "DWORD";
case kQuad:
return "QWORD";
default:
UNREACHABLE();
}
#else
switch (directive) {
case kByte:
return ".byte";
@ -40,17 +66,6 @@ const char* PlatformDependentEmbeddedFileWriter::DirectiveAsString(
return ".octa";
}
UNREACHABLE();
#else
switch (directive) {
case kByte:
return "BYTE";
case kLong:
return "DWORD";
case kQuad:
return "QWORD";
default:
UNREACHABLE();
}
#endif
}
@ -209,11 +224,12 @@ int PlatformDependentEmbeddedFileWriter::IndentedDataDirective(
return fprintf(fp_, " %s ", DirectiveAsString(directive));
}
// V8_OS_WIN
// V8_OS_WIN (MSVC)
// -----------------------------------------------------------------------------
#elif defined(V8_OS_WIN)
#elif defined(V8_OS_WIN) && defined(V8_COMPILER_IS_MSVC)
// For MSVC builds we emit assembly in MASM syntax.
// See https://docs.microsoft.com/en-us/cpp/assembler/masm/directives-reference.
void PlatformDependentEmbeddedFileWriter::SectionText() {
@ -231,32 +247,20 @@ void PlatformDependentEmbeddedFileWriter::SectionRoData() {
void PlatformDependentEmbeddedFileWriter::DeclareUint32(const char* name,
uint32_t value) {
DeclareSymbolGlobal(name);
#if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM64)
fprintf(fp_, "%s %s %d\n", name, DirectiveAsString(kLong), value);
#else
fprintf(fp_, "_%s %s %d\n", name, DirectiveAsString(kLong), value);
#endif
fprintf(fp_, "%s%s %s %d\n", SYMBOL_PREFIX, name, DirectiveAsString(kLong),
value);
}
void PlatformDependentEmbeddedFileWriter::DeclarePointerToSymbol(
const char* name, const char* target) {
DeclareSymbolGlobal(name);
#if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM64)
fprintf(fp_, "%s %s %s\n", name, DirectiveAsString(PointerSizeDirective()),
target);
#else
fprintf(fp_, "_%s %s _%s\n", name, DirectiveAsString(PointerSizeDirective()),
target);
#endif
fprintf(fp_, "%s%s %s %s%s\n", SYMBOL_PREFIX, name,
DirectiveAsString(PointerSizeDirective()), SYMBOL_PREFIX, target);
}
void PlatformDependentEmbeddedFileWriter::DeclareSymbolGlobal(
const char* name) {
#if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM64)
fprintf(fp_, "PUBLIC %s\n", name);
#else
fprintf(fp_, "PUBLIC _%s\n", name);
#endif
fprintf(fp_, "PUBLIC %s%s\n", SYMBOL_PREFIX, name);
}
void PlatformDependentEmbeddedFileWriter::AlignToCodeAlignment() {
@ -270,28 +274,17 @@ void PlatformDependentEmbeddedFileWriter::Comment(const char* string) {
}
void PlatformDependentEmbeddedFileWriter::DeclareLabel(const char* name) {
#if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM64)
fprintf(fp_, "%s LABEL %s\n", name, DirectiveAsString(kByte));
#else
fprintf(fp_, "_%s LABEL %s\n", name, DirectiveAsString(kByte));
#endif
fprintf(fp_, "%s%s LABEL %s\n", SYMBOL_PREFIX, name,
DirectiveAsString(kByte));
}
void PlatformDependentEmbeddedFileWriter::DeclareFunctionBegin(
const char* name) {
#if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM64)
fprintf(fp_, "%s PROC\n", name);
#else
fprintf(fp_, "_%s PROC\n", name);
#endif
fprintf(fp_, "%s%s PROC\n", SYMBOL_PREFIX, name);
}
void PlatformDependentEmbeddedFileWriter::DeclareFunctionEnd(const char* name) {
#if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM64)
fprintf(fp_, "%s ENDP\n", name);
#else
fprintf(fp_, "_%s ENDP\n", name);
#endif
fprintf(fp_, "%s%s ENDP\n", SYMBOL_PREFIX, name);
}
int PlatformDependentEmbeddedFileWriter::HexLiteral(uint64_t value) {
@ -313,7 +306,7 @@ int PlatformDependentEmbeddedFileWriter::IndentedDataDirective(
return fprintf(fp_, " %s ", DirectiveAsString(directive));
}
// Everything but AIX, Windows, or OSX.
// Everything but AIX, Windows with MSVC, or OSX.
// -----------------------------------------------------------------------------
#else
@ -347,12 +340,13 @@ void PlatformDependentEmbeddedFileWriter::DeclarePointerToSymbol(
const char* name, const char* target) {
DeclareSymbolGlobal(name);
DeclareLabel(name);
fprintf(fp_, " %s %s\n", DirectiveAsString(PointerSizeDirective()), target);
fprintf(fp_, " %s %s%s\n", DirectiveAsString(PointerSizeDirective()),
SYMBOL_PREFIX, target);
}
void PlatformDependentEmbeddedFileWriter::DeclareSymbolGlobal(
const char* name) {
fprintf(fp_, ".global %s\n", name);
fprintf(fp_, ".global %s%s\n", SYMBOL_PREFIX, name);
}
void PlatformDependentEmbeddedFileWriter::AlignToCodeAlignment() {
@ -364,14 +358,23 @@ void PlatformDependentEmbeddedFileWriter::Comment(const char* string) {
}
void PlatformDependentEmbeddedFileWriter::DeclareLabel(const char* name) {
fprintf(fp_, "%s:\n", name);
fprintf(fp_, "%s%s:\n", SYMBOL_PREFIX, name);
}
void PlatformDependentEmbeddedFileWriter::DeclareFunctionBegin(
const char* name) {
DeclareLabel(name);
#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_ARM64)
#if defined(V8_OS_WIN)
// The directives for inserting debugging information on Windows come
// from the PE (Portable Executable) and COFF (Common Object File Format)
// standards. Documented here:
// https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format
//
// .scl 2 means StorageClass external.
// .type 32 means Type Representation Function.
fprintf(fp_, ".def %s%s; .scl 2; .type 32; .endef;\n", SYMBOL_PREFIX, name);
#elif defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_ARM64)
// ELF format binaries on ARM use ".type <function name>, %function"
// to create a DWARF subprogram entry.
fprintf(fp_, ".type %s, %%function\n", name);
@ -400,5 +403,8 @@ int PlatformDependentEmbeddedFileWriter::IndentedDataDirective(
#endif
#undef SYMBOL_PREFIX
#undef V8_COMPILER_IS_MSVC
} // namespace internal
} // namespace v8

View File

@ -59,8 +59,6 @@ class PlatformDependentEmbeddedFileWriter final {
private:
void DeclareSymbolGlobal(const char* name);
static const char* DirectiveAsString(DataDirective directive);
private:
FILE* fp_ = nullptr;
};
@ -76,8 +74,8 @@ class PlatformDependentEmbeddedFileWriter final {
// The variant is usually "Default" but can be modified in multisnapshot builds.
class EmbeddedFileWriter {
public:
void SetEmbeddedFile(const char* embedded_cpp_file) {
embedded_cpp_path_ = embedded_cpp_file;
void SetEmbeddedFile(const char* embedded_src_path) {
embedded_src_path_ = embedded_src_path;
}
void SetEmbeddedVariant(const char* embedded_variant) {
@ -90,9 +88,9 @@ class EmbeddedFileWriter {
private:
void MaybeWriteEmbeddedFile(const i::EmbeddedData* blob) const {
if (embedded_cpp_path_ == nullptr) return;
if (embedded_src_path_ == nullptr) return;
FILE* fp = GetFileDescriptorOrDie(embedded_cpp_path_);
FILE* fp = GetFileDescriptorOrDie(embedded_src_path_);
PlatformDependentEmbeddedFileWriter writer;
writer.SetFile(fp);
@ -203,14 +201,17 @@ class EmbeddedFileWriter {
w->FileEpilogue();
}
#ifdef V8_OS_WIN
#if defined(_MSC_VER) && !defined(__clang__)
#define V8_COMPILER_IS_MSVC
#endif
#ifdef V8_COMPILER_IS_MSVC
// Windows MASM doesn't have an .octa directive, use QWORDs instead.
// Note: MASM *really* does not like large data streams. It takes over 5
// minutes to assemble the ~350K lines of embedded.S produced when using
// BYTE directives in a debug build. QWORD produces roughly 120KLOC and
// reduces assembly time to ~40 seconds. Still terrible, but much better
// than before.
// TODO(v8:8475): Use nasm or yasm instead of MASM.
// than before. See also: https://crbug.com/v8/8475.
static constexpr DataDirective kByteChunkDirective = kQuad;
static constexpr int kByteChunkSize = 8;
@ -220,7 +221,7 @@ class EmbeddedFileWriter {
const uint64_t* quad_ptr = reinterpret_cast<const uint64_t*>(data);
return current_line_length + w->HexLiteral(*quad_ptr);
}
#else // V8_OS_WIN
#else // V8_COMPILER_IS_MSVC
static constexpr DataDirective kByteChunkDirective = kOcta;
static constexpr int kByteChunkSize = 16;
@ -245,7 +246,8 @@ class EmbeddedFileWriter {
}
return current_line_length;
}
#endif // V8_OS_WIN
#endif // V8_COMPILER_IS_MSVC
#undef V8_COMPILER_IS_MSVC
static int WriteDirectiveOrSeparator(PlatformDependentEmbeddedFileWriter* w,
int current_line_length,
@ -303,7 +305,7 @@ class EmbeddedFileWriter {
if (current_line_length != 0) w->Newline();
}
const char* embedded_cpp_path_ = nullptr;
const char* embedded_src_path_ = nullptr;
const char* embedded_variant_ = kDefaultEmbeddedVariant;
};

View File

@ -0,0 +1,29 @@
#!/usr/bin/env python
# Copyright 2018 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
'''
Converts a given file in clang assembly syntax to a corresponding
representation in inline assembly. Specifically, this is used to convert
embedded.S to embedded.cc for Windows clang builds.
'''
import argparse
import sys
def asm_to_inl_asm(in_filename, out_filename):
with open(in_filename, 'r') as infile, open(out_filename, 'wb') as outfile:
outfile.write('__asm__(\n')
for line in infile:
outfile.write(' "%s\\n"\n' % line.rstrip())
outfile.write(');\n')
return 0
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('input', help='Name of the input assembly file')
parser.add_argument('output', help='Name of the target CC file')
args = parser.parse_args()
sys.exit(asm_to_inl_asm(args.input, args.output))