Skip to content

Commit 0f78a54

Browse files
authored
Enable Bytecode regen on Linux/MacOS (#6425)
Introduce a Force32BitByteCode flag usable in Debug and Test builds that forces a 64bit CC build to generate 32bit bytecode for the known differences. (All related logic is #ifdef'd out in release builds) Add a python script that will use that flag to perform the regen on macos & linux.
1 parent 63e5d6b commit 0f78a54

10 files changed

+160
-1
lines changed

RegenAllByteCode.cmd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
::-------------------------------------------------------------------------------------------------------
55

66
:: Regenerate all bytecode.
7+
:: Note, this script is windows only, on linux or macOS please use tools/xplatRegenByteCode.py
78
:: ch.exe is used to generate Intl bytecodes.
89
:: ch.exe (NoJIT variety) is used to generate NoJIT Intl bytecodes.
910
:: Each set of bytecode requires an x86_debug and x64_debug binary.

RegenAllByteCodeNoBuild.cmd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
:: every flavor of ChakraCore.dll when there are no relevant changes is a waste of time.
1111
:: Please ensure that you use buddy builds to validate the results.
1212

13+
:: Note, this script is windows only, on linux or macOS please use tools/xplatRegenByteCode.py
1314
:: Regenerate all bytecode (without rebuilding each flavor of ch.exe)
1415
:: ch.exe is used to generate Intl bytecodes.
1516
:: ch.exe (NoJIT variety) is used to generate NoJIT Intl bytecodes.

lib/Common/ConfigFlagsList.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,9 @@ FLAGR(Boolean, SkipSplitOnNoResult, "If the result of Regex split isn't used, sk
11011101
#ifdef TEST_ETW_EVENTS
11021102
FLAGNR(String, TestEtwDll , "Path of the TestEtwEventSink DLL", nullptr)
11031103
#endif
1104+
#ifdef ENABLE_TEST_HOOKS
1105+
FLAGNR(Boolean, Force32BitByteCode, "Force CC to generate 32bit bytecode intended only for regenerating bytecode headers.", false)
1106+
#endif
11041107

11051108
FLAGNR(Boolean, CollectGarbage , "Enable CollectGarbage API", DEFAULT_CONFIG_CollectGarbage)
11061109

lib/Runtime/Base/FunctionBody.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4410,7 +4410,13 @@ namespace Js
44104410
void FunctionBody::RecordIntConstant(RegSlot location, unsigned int val)
44114411
{
44124412
ScriptContext *scriptContext = this->GetScriptContext();
4413+
#ifdef ENABLE_TEST_HOOKS
4414+
Var intConst = scriptContext->GetConfig()->Force32BitByteCode() ?
4415+
JavascriptNumber::ToVarFor32BitBytecode((int32)val, scriptContext) :
4416+
JavascriptNumber::ToVar((int32)val, scriptContext);
4417+
#else
44134418
Var intConst = JavascriptNumber::ToVar((int32)val, scriptContext);
4419+
#endif
44144420
this->RecordConstant(location, intConst);
44154421
}
44164422

lib/Runtime/Base/ThreadConfigFlagsList.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ FLAG_RELEASE(IsESExportNsAsEnabled, ESExportNsAs)
5454
FLAG_RELEASE(IsESSymbolDescriptionEnabled, ESSymbolDescription)
5555
FLAG_RELEASE(IsESGlobalThisEnabled, ESGlobalThis)
5656
FLAG_RELEASE(IsES2018AsyncIterationEnabled, ES2018AsyncIteration)
57+
#ifdef ENABLE_TEST_HOOKS
58+
FLAG_RELEASE(Force32BitByteCode, Force32BitByteCode)
59+
#endif
5760
#ifdef ENABLE_PROJECTION
5861
FLAG(AreWinRTDelegatesInterfaces, WinRTDelegateInterfaces)
5962
FLAG_RELEASE(IsWinRTAdaptiveAppsEnabled, WinRTAdaptiveApps)

lib/Runtime/ByteCode/ByteCodeEmitter.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10140,7 +10140,11 @@ void EmitBinary(Js::OpCode opcode, ParseNode *pnode, ByteCodeGenerator *byteCode
1014010140
byteCodeGenerator->EndStatement(pnode);
1014110141
}
1014210142

10143-
bool CollectConcat(ParseNode *pnodeAdd, DListCounted<ParseNode *, ArenaAllocator>& concatOpnds, ArenaAllocator *arenaAllocator)
10143+
bool CollectConcat(ParseNode *pnodeAdd, DListCounted<ParseNode *, ArenaAllocator>& concatOpnds, ArenaAllocator *arenaAllocator
10144+
#ifdef ENABLE_TEST_HOOKS
10145+
, bool Force32BitByteCode = false
10146+
#endif
10147+
)
1014410148
{
1014510149
Assert(pnodeAdd->nop == knopAdd);
1014610150
Assert(pnodeAdd->CanFlattenConcatExpr());
@@ -10161,7 +10165,18 @@ bool CollectConcat(ParseNode *pnodeAdd, DListCounted<ParseNode *, ArenaAllocator
1016110165

1016210166
// Detect if there are any string larger then the append size limit.
1016310167
// If there are, we can do concat; otherwise, still use add so we will not lose the AddLeftDead opportunities.
10168+
#ifdef ENABLE_TEST_HOOKS
10169+
if (Force32BitByteCode)
10170+
{
10171+
doConcatString = doConcatString || (pnode->AsParseNodeStr()->pid->Cch() > 4);
10172+
}
10173+
else
10174+
{
10175+
doConcatString = doConcatString || !Js::CompoundString::ShouldAppendChars(pnode->AsParseNodeStr()->pid->Cch());
10176+
}
10177+
#else
1016410178
doConcatString = doConcatString || !Js::CompoundString::ShouldAppendChars(pnode->AsParseNodeStr()->pid->Cch());
10179+
#endif
1016510180
}
1016610181
else
1016710182
{
@@ -10233,7 +10248,12 @@ void EmitAdd(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *f
1023310248
// We should only have a string concat if the feature is on.
1023410249
Assert(!PHASE_OFF1(Js::ByteCodeConcatExprOptPhase));
1023510250
DListCounted<ParseNode*, ArenaAllocator> concatOpnds(byteCodeGenerator->GetAllocator());
10251+
#ifdef ENABLE_TEST_HOOKS
10252+
bool doConcatString = CollectConcat(pnode, concatOpnds, byteCodeGenerator->GetAllocator(),
10253+
byteCodeGenerator->GetScriptContext()->GetConfig()->Force32BitByteCode());
10254+
#else
1023610255
bool doConcatString = CollectConcat(pnode, concatOpnds, byteCodeGenerator->GetAllocator());
10256+
#endif
1023710257
if (doConcatString)
1023810258
{
1023910259
uint concatCount = concatOpnds.Count();

lib/Runtime/ByteCode/ByteCodeSerializer.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,12 @@ class ByteCodeBufferBuilder
488488
{
489489
expectedFunctionBodySize.value = 0;
490490
expectedOpCodeCount.value = 0;
491+
#ifdef ENABLE_TEST_HOOKS
492+
if (scriptContext->GetConfig()->Force32BitByteCode())
493+
{
494+
architecture.value = 32;
495+
}
496+
#endif
491497
}
492498

493499
// Library bytecode uses its own scheme

lib/Runtime/Library/JavascriptNumber.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ namespace Js
3737
static Var ToVarMaybeInPlace(double value, ScriptContext* scriptContext, JavascriptNumber *result);
3838
static Var ToVarIntCheck(double value, ScriptContext* scriptContext);
3939
static Var ToVar(int32 nValue, ScriptContext* scriptContext);
40+
#ifdef ENABLE_TEST_HOOKS
41+
static Var ToVarFor32BitBytecode(int32 nValue, ScriptContext* scriptContext);
42+
#endif
4043
#if defined(__clang__) && defined(_M_IX86)
4144
static Var ToVar(intptr_t nValue, ScriptContext* scriptContext);
4245
#endif

lib/Runtime/Library/JavascriptNumber.inl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,17 @@ namespace Js
4141
}
4242
}
4343

44+
#ifdef ENABLE_TEST_HOOKS
45+
__forceinline Var JavascriptNumber::ToVarFor32BitBytecode(int32 nValue, ScriptContext* scriptContext)
46+
{
47+
if ((1073741824 > nValue) && (nValue > -1073741824))
48+
{
49+
return TaggedInt::ToVarUnchecked(nValue);
50+
}
51+
return JavascriptNumber::NewInlined((double) nValue, scriptContext);
52+
}
53+
#endif
54+
4455
#if defined(__clang__) && defined(_M_IX86)
4556
__forceinline Var JavascriptNumber::ToVar(intptr_t nValue, ScriptContext* scriptContext)
4657
{

tools/xplatRegenByteCode.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#!/usr/bin/env python
2+
#-------------------------------------------------------------------------------------------------------
3+
# Copyright (C) Microsoft. All rights reserved.
4+
# Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
5+
#-------------------------------------------------------------------------------------------------------
6+
7+
# Regenerate embedded bytecode headers.
8+
# NOTEs:
9+
# 1. this script is for linux and macOS only, on windows please use RegenAllByteCode.cmd AND update_bytecode_version.ps1
10+
# 2. this script uses paths relative to the tools directory it's in so cd to it before running
11+
# 3. this script relies on forcing 64bit CC builds to produce 32bit bytecode - this could break due to future changes to CC.
12+
# If this facility breaks the CI will fail AND either this will need fixing or bytecode will need to be regenerated using
13+
# 32 bit builds on windows
14+
# 4. Run with flag '--skip-build' if the necessary versions of ChakraCore are already built to skip the compilation step
15+
# (this is useful if editing the .js files that the bytecode is produced from)
16+
# Two versions of CC and ch must be compiled for bytecode generation
17+
# ch is used to generate bytecode.
18+
# ch (NoJIT variety) is used to generate NoJIT bytecodes.
19+
20+
import subprocess
21+
import sys
22+
import uuid
23+
24+
# Compile ChakraCore both noJit and Jit variants
25+
def run_sub(message, commands, error):
26+
print(message)
27+
sub = subprocess.Popen(commands)
28+
sub.wait()
29+
if sub.returncode != 0:
30+
sys.exit(error)
31+
32+
if len(sys.argv) == 1 or sys.argv[1] != '--skip-build':
33+
run_sub('Compiling ChakraCore with no Jit',
34+
['../build.sh', '--no-jit', '--test-build', '--target-path=../out/noJit', '-j=2'],
35+
'No Jit build failed - aborting bytecode generation')
36+
run_sub('Compiling ChakraCore with Jit',
37+
['../build.sh', '--test-build', '--target-path=../out', '-j=2'],
38+
'Jit build failed - aborting bytecode generation')
39+
40+
# Regenerate the bytecode
41+
def bytecode_job(outPath, command, error):
42+
header = open(outPath, 'w')
43+
job = subprocess.Popen(command, stdout=header)
44+
job.wait()
45+
if job.returncode != 0:
46+
sys.exit(error)
47+
48+
# INTL
49+
print('Generating INTL bytecode')
50+
bytecode_job('../lib/Runtime/Library/InJavascript/Intl.js.nojit.bc.64b.h',
51+
['../out/noJit/test/ch', '-GenerateLibraryByteCodeHeader', '-Intl', '../lib/Runtime/Library/InJavascript/Intl.js'],
52+
'Failed to generate INTL 64bit noJit bytecode')
53+
54+
bytecode_job('../lib/Runtime/Library/InJavascript/Intl.js.nojit.bc.32b.h',
55+
['../out/noJit/test/ch', '-GenerateLibraryByteCodeHeader', '-Intl', '-Force32BitByteCode','../lib/Runtime/Library/InJavascript/Intl.js'],
56+
'Failed to generate INTL 32bit noJit bytecode')
57+
58+
bytecode_job('../lib/Runtime/Library/InJavascript/Intl.js.bc.64b.h',
59+
['../out/test/ch', '-GenerateLibraryByteCodeHeader', '-Intl', '../lib/Runtime/Library/InJavascript/Intl.js'],
60+
'Failed to generate INTL 64bit bytecode')
61+
62+
bytecode_job('../lib/Runtime/Library/InJavascript/Intl.js.bc.32b.h',
63+
['../out/test/ch', '-GenerateLibraryByteCodeHeader', '-Intl', '-Force32BitByteCode','../lib/Runtime/Library/InJavascript/Intl.js'],
64+
'Failed to generate INTL 32bit bytecode')
65+
66+
# JsBuiltin
67+
print('Generating JsBuiltin Bytecode')
68+
bytecode_job('../lib/Runtime/Library/JsBuiltin/JsBuiltin.js.nojit.bc.64b.h',
69+
['../out/noJit/test/ch', '-GenerateLibraryByteCodeHeader', '-JsBuiltIn', '-LdChakraLib', '../lib/Runtime/Library/JsBuiltin/JsBuiltin.js'],
70+
'Failed to generate noJit 64bit JsBuiltin Bytecode')
71+
72+
bytecode_job('../lib/Runtime/Library/JsBuiltin/JsBuiltin.js.nojit.bc.32b.h',
73+
['../out/noJit/test/ch', '-GenerateLibraryByteCodeHeader', '-JsBuiltIn', '-LdChakraLib', '-Force32BitByteCode', '../lib/Runtime/Library/JsBuiltin/JsBuiltin.js'],
74+
'Failed to generate noJit 32bit JsBuiltin Bytecode')
75+
76+
bytecode_job('../lib/Runtime/Library/JsBuiltin/JsBuiltin.js.bc.64b.h',
77+
['../out/test/ch', '-GenerateLibraryByteCodeHeader', '-JsBuiltIn', '-LdChakraLib', '../lib/Runtime/Library/JsBuiltin/JsBuiltin.js'],
78+
'Failed to generate 64bit JsBuiltin Bytecode')
79+
80+
bytecode_job('../lib/Runtime/Library/JsBuiltin/JsBuiltin.js.bc.32b.h',
81+
['../out/test/ch', '-GenerateLibraryByteCodeHeader', '-JsBuiltIn', '-LdChakraLib', '-Force32BitByteCode', '../lib/Runtime/Library/JsBuiltin/JsBuiltin.js'],
82+
'Failed to generate 32bit JsBuiltin Bytecode')
83+
84+
# Bytecode regeneration complete - create a new GUID for it
85+
print('Generating new GUID for new bytecode')
86+
guid_header = open('../lib/Runtime/Bytecode/ByteCodeCacheReleaseFileVersion.h', 'w')
87+
guid = str(uuid.uuid4())
88+
89+
output_str = '''//-------------------------------------------------------------------------------------------------------
90+
// Copyright (C) Microsoft. All rights reserved.
91+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
92+
//-------------------------------------------------------------------------------------------------------
93+
// NOTE: If there is a merge conflict the correct fix is to make a new GUID.
94+
// This file was generated with tools/xplatRegenByteCode.py
95+
96+
// {%s}
97+
const GUID byteCodeCacheReleaseFileVersion =
98+
{ 0x%s, 0x%s, 0x%s, {0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s } };
99+
''' % (guid,
100+
guid[:8], guid[9:13], guid[14:18], guid[19:21], guid[21:23], guid[24:26],
101+
guid[26:28], guid[28:30], guid[30:32], guid[32:34], guid[-2:])
102+
103+
guid_header.write(output_str)
104+
105+
print('Bytecode successfully regenerated. Please rebuild ChakraCore to incorporate it.')

0 commit comments

Comments
 (0)