Merge pull request #17226 from weshaggard/MergeRestoreTargets
authorWes Haggard <weshaggard@users.noreply.github.com>
Tue, 27 Mar 2018 00:28:31 +0000 (17:28 -0700)
committerGitHub <noreply@github.com>
Tue, 27 Mar 2018 00:28:31 +0000 (17:28 -0700)
Merge various restore tasks into one Sync target

35 files changed:
build.cmd
build.sh
dependencies.props
netci.groovy
src/gc/env/gcenv.base.h
src/inc/eventtracebase.h
src/inc/utilcode.h
src/jit/codegenxarch.cpp
src/jit/compiler.h
src/jit/compiler.hpp
src/jit/decomposelongs.cpp
src/jit/flowgraph.cpp
src/jit/gentree.cpp
src/jit/gentree.h
src/jit/hwintrinsiclistxarch.h
src/jit/importer.cpp
src/jit/lower.cpp
src/jit/lowerarmarch.cpp
src/jit/lowerxarch.cpp
src/jit/morph.cpp
src/jit/optimizer.cpp
src/jit/simd.cpp
src/mscorlib/shared/System/Globalization/CompareInfo.cs
src/pal/src/CMakeLists.txt
src/pal/src/configure.cmake
src/vm/corhost.cpp
src/vm/eetwain.cpp
tests/runtest.sh
tests/runtesttilstable.sh [new file with mode: 0755]
tests/scripts/arm32_ci_script.sh
tests/src/JIT/Regression/JitBlue/GitHub_13501/GitHub_13501.il [new file with mode: 0644]
tests/src/JIT/Regression/JitBlue/GitHub_13501/GitHub_13501.ilproj [new file with mode: 0644]
tests/src/performance/Scenario/JitBench/Benchmarks/WebAppBenchmarks.cs
tests/src/sizeondisk/sodbench/SoDBench.cs
tests/testsFailing.arm.txt [new file with mode: 0644]

index ff1e28e..be6b2a7 100644 (file)
--- a/build.cmd
+++ b/build.cmd
@@ -22,6 +22,11 @@ if defined VS150COMNTOOLS (
   set __VSVersion=vs2015
 )
 
+:: Work around Jenkins CI + msbuild problem: Jenkins sometimes creates very large environment
+:: variables, and msbuild can't handle environment blocks with such large variables. So clear
+:: out the variables that might be too large.
+set ghprbCommentBody=
+
 :: Note that the msbuild project files (specifically, dir.proj) will use the following variables, if set:
 ::      __BuildArch         -- default: x64
 ::      __BuildType         -- default: Debug
index 9ea292e..1cb0d7e 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -1,5 +1,10 @@
 #!/usr/bin/env bash
 
+# Work around Jenkins CI + msbuild problem: Jenkins sometimes creates very large environment
+# variables, and msbuild can't handle environment blocks with such large variables. So clear
+# out the variables that might be too large.
+export ghprbCommentBody=
+
 # resolve python-version to use
 if [ "$PYTHON" == "" ] ; then
     if ! PYTHON=$(command -v python2.7 || command -v python2 || command -v python)
index cb0d690..ba10aa3 100644 (file)
@@ -23,7 +23,7 @@
 
   <!-- Source of truth for dependency tooling: the commit hash of the dotnet/versions master branch as of the last auto-upgrade. -->
   <PropertyGroup>
-    <CoreFxCurrentRef>d2563d3a72fd072e656e1a2806f5cd14fabcca2a</CoreFxCurrentRef>
+    <CoreFxCurrentRef>64cd7256fa7fa2a124d7d2d86bb1cf500bf515a2</CoreFxCurrentRef>
     <CoreClrCurrentRef>9171e74940c4ce8054acd8995f93a096c613dafa</CoreClrCurrentRef>
     <BuildToolsCurrentRef>7f2023d4b56b28175c9d7d07a8359cf7e0a131be</BuildToolsCurrentRef>
     <PgoDataCurrentRef>553f30357d08df4ed6d32f70f3478a76f5db79c2</PgoDataCurrentRef>
@@ -31,8 +31,8 @@
 
   <!-- Tests/infrastructure dependency versions. -->
   <PropertyGroup>
-    <MicrosoftPrivateCoreFxNETCoreAppPackageVersion>4.5.0-preview3-26326-01</MicrosoftPrivateCoreFxNETCoreAppPackageVersion>
-    <MicrosoftNETCorePlatformsPackageVersion>2.1.0-preview3-26326-01</MicrosoftNETCorePlatformsPackageVersion>
+    <MicrosoftPrivateCoreFxNETCoreAppPackageVersion>4.5.0-preview3-26326-03</MicrosoftPrivateCoreFxNETCoreAppPackageVersion>
+    <MicrosoftNETCorePlatformsPackageVersion>2.1.0-preview3-26326-03</MicrosoftNETCorePlatformsPackageVersion>
     <PgoDataPackageVersion>99.99.99-master-20180228-0037</PgoDataPackageVersion>
     <MicrosoftNETCoreRuntimeCoreCLRPackageVersion>2.1.0-preview3-26323-05</MicrosoftNETCoreRuntimeCoreCLRPackageVersion>
     <XunitPackageVersion>2.2.0-beta2-build3300</XunitPackageVersion>
@@ -45,7 +45,7 @@
     <!-- Scenario tests install this version of Microsoft.NetCore.App, then patch coreclr binaries via xcopy. At the moment it is
          updated manually whenever breaking changes require it to move forward, but it would be nice if we could update it automatically
          as we do with many of the package versions above -->
-    <BaselineMicrosoftNetCoreAppPackageVersion>2.1.0-preview2-26131-06</BaselineMicrosoftNetCoreAppPackageVersion>
+    <BaselineMicrosoftNetCoreAppPackageVersion>2.1.0-preview3-26319-04</BaselineMicrosoftNetCoreAppPackageVersion>
   </PropertyGroup>
 
   <!-- Package versions used as toolsets -->
index 886b462..9186459 100755 (executable)
@@ -56,7 +56,13 @@ class Constants {
                'Fedora24',
                'Tizen']
 
-    def static crossList = ['Ubuntu', 'OSX10.12', 'CentOS7.1', 'RHEL7.2', 'Debian8.4', 'Windows_NT']
+    def static crossList = [
+               'Ubuntu',
+               'Debian8.4',
+               'OSX10.12',
+               'Windows_NT',
+               'CentOS7.1',
+               'RHEL7.2']
 
     // This is a set of JIT stress modes combined with the set of variables that
     // need to be set to actually enable that stress mode.  The key of the map is the stress mode and
@@ -169,7 +175,7 @@ class Constants {
                 'Checked'
             ]
         ],
-        'Windows_NT_buildOnly': [
+        'Windows_NT_BuildOnly': [
             'x64': [
                 'Checked',
                 'Release'
@@ -178,6 +184,9 @@ class Constants {
                 'Checked',
                 'Release'
             ], 
+            'arm': [
+                'Checked'
+            ], 
         ],
         'Ubuntu': [
             'x64': [
@@ -185,6 +194,9 @@ class Constants {
             ],
             'arm64': [
                 'Debug'
+            ],
+            'arm': [
+                'Checked'
             ]
         ],
         'CentOS7.1': [
@@ -199,7 +211,7 @@ class Constants {
             ]
         ],
         'Tizen': [
-            'arm': [
+            'armem': [
                 'Checked'
             ]
         ],
@@ -317,17 +329,76 @@ class Constants {
     ]
   
     def static validLinuxArm64Scenarios = [ 
-        'normal',
-        'r2r',
-        'innerloop',
-        'gcstress0x3',
-        'gcstress0xc'
+               'innerloop',
+               'normal',
+               'r2r',
+               'gcstress0x3',
+               'gcstress0xc'
+    ]
+
+    // Note: no GCStress-related scenario is enabled currently.
+    def static validLinuxArmScenarios = [
+               'innerloop',
+               'normal',
+               'r2r',
+               'r2r_jitstress1',
+               'r2r_jitstress2',
+               'r2r_jitstressregs1',
+               'r2r_jitstressregs2',
+               'r2r_jitstressregs3',
+               'r2r_jitstressregs4',
+               'r2r_jitstressregs8',
+               'r2r_jitstressregs0x10',
+               'r2r_jitstressregs0x80',
+               'r2r_jitstressregs0x1000',
+               'r2r_jitminopts',
+               'r2r_jitforcerelocs',
+               // 'r2r_gcstress15',
+               'minopts',
+               'forcerelocs',
+               'jitstress1',
+               'jitstress2',
+               'jitstressregs1',
+               'jitstressregs2',
+               'jitstressregs3',
+               'jitstressregs4',
+               'jitstressregs8',
+               'jitstressregs0x10',
+               'jitstressregs0x80',
+               'jitstressregs0x1000',
+               'jitstress2_jitstressregs1',
+               'jitstress2_jitstressregs2',
+               'jitstress2_jitstressregs3',
+               'jitstress2_jitstressregs4',
+               'jitstress2_jitstressregs8',
+               'jitstress2_jitstressregs0x10',
+               'jitstress2_jitstressregs0x80',
+               'jitstress2_jitstressregs0x1000',
+               'tailcallstress'
+               // 'gcstress0x3',
+               // 'gcstress0xc',
+               // 'zapdisable',
+               // 'heapverify1',
+               // 'gcstress0xc_zapdisable',
+               // 'gcstress0xc_zapdisable_jitstress2',
+               // 'gcstress0xc_zapdisable_heapverify1',
+               // 'gcstress0xc_jitstress1',
+               // 'gcstress0xc_jitstress2',
+               // 'gcstress0xc_minopts_heapverify1'
     ]
 
     def static configurationList = ['Debug', 'Checked', 'Release']
 
     // This is the set of architectures
-    def static architectureList = ['arm', 'armlb', 'x86_arm_altjit', 'x64_arm64_altjit', 'arm64', 'x64', 'x86']
+    // Some of these are pseudo-architectures:
+    //    armlb -- same as arm, but use the LEGACY_BACKEND JIT
+    //    armem -- ARM builds/runs using an emulator. Used for Ubuntu/Ubuntu16.04/Tizen runs.
+    //    x86_arm_altjit -- ARM runs on x86 using the ARM altjit
+    //    x64_arm64_altjit -- ARM64 runs on x64 using the ARM64 altjit
+    def static architectureList = ['arm', 'armlb', 'armem', 'x86_arm_altjit', 'x64_arm64_altjit', 'arm64', 'x64', 'x86']
+
+    // This set of architectures that cross build on Windows and run on Windows ARM64 hardware.
+    def static armWindowsCrossArchitectureList = ['arm', 'armlb', 'arm64']
 }
 
 // **************************************************************
@@ -465,7 +536,7 @@ def static setMachineAffinity(def job, def os, def architecture, def options = n
     assert os instanceof String
     assert architecture instanceof String
 
-    def armArches = ['arm', 'armlb', 'arm64']
+    def armArches = ['arm', 'armlb', 'armem', 'arm64']
     def supportedArmLinuxOs = ['Ubuntu', 'Ubuntu16.04', 'Tizen']
 
     if (!(architecture in armArches)) {
@@ -482,9 +553,9 @@ def static setMachineAffinity(def job, def os, def architecture, def options = n
     // Windows_NT
     // 
     // Arm32 (Build) -> latest-arm64
-    //       |-> os == "Windows_NT" && architecture == "arm" || architecture == "armlb" && options['use_arm64_build_machine'] == true
+    //       |-> os == "Windows_NT" && (architecture == "arm" || architecture == "armlb") && options['use_arm64_build_machine'] == true
     // Arm32 (Test)  -> arm64-windows_nt
-    //       |-> os == "Windows_NT" && architecture == "arm" || architecture == "armlb" && options['use_arm64_build_machine'] == false
+    //       |-> os == "Windows_NT" && (architecture == "arm" || architecture == "armlb") && options['use_arm64_build_machine'] == false
     //
     // Arm64 (Build) -> latest-arm64
     //       |-> os == "Windows_NT" && architecture == "arm64" && options['use_arm64_build_machine'] == true
@@ -493,10 +564,15 @@ def static setMachineAffinity(def job, def os, def architecture, def options = n
     //
     // Ubuntu
     //
-    // Arm32 (Build) -> arm-cross-latest
-    //       |-> os in supportedArmLinuxOs && architecture == "arm" || architecture == "armlb"
-    // Arm32 (Test)  -> NYI Arch not supported
-    //       |->
+    // Arm32 emulator (Build, Test) -> arm-cross-latest
+    //       |-> os in supportedArmLinuxOs && (architecture == "armem")
+    //
+    // Arm32 hardware (Flow) -> Ubuntu 16.04 latest-or-auto (don't use limited arm hardware)
+    //       |-> os == "Ubuntu" && (architecture == "arm") && options['is_flow_job'] == true
+    // Arm32 hardware (Build) -> Ubuntu 16.04 latest-or-auto
+    //       |-> os == "Ubuntu" && (architecture == "arm") && options['is_build_job'] == true
+    // Arm32 hardware (Test) -> ubuntu.1404.arm32.open
+    //       |-> os == "Ubuntu" && (architecture == "arm")
     //
     // Arm64 (Build) -> arm64-cross-latest
     //       |-> os != "Windows_NT" && architecture == "arm64" && options['is_build_only'] == true
@@ -508,7 +584,7 @@ def static setMachineAffinity(def job, def os, def architecture, def options = n
     // This has to be a arm arch
     assert architecture in armArches
     if (os == "Windows_NT") {
-        // Arm(64) Windows jobs share the same machines for now
+        // arm32/arm64 Windows jobs share the same machines for now
         def isBuild = options['use_arm64_build_machine'] == true
 
         if (isBuild == true) {
@@ -520,24 +596,90 @@ def static setMachineAffinity(def job, def os, def architecture, def options = n
         assert os != 'Windows_NT'
         assert os in supportedArmLinuxOs
 
-        if (architecture == 'arm' || architecture == 'armlb') {
-            Utilities.setMachineAffinity(job, 'Ubuntu', 'arm-cross-latest')
-        } else {
-            // Arm64 Linux
-            if (options['is_build_only'] == true) {
+        if (architecture == 'arm64') {
+            if ((options != null) && (options['is_build_only'] == true)) {
+                // Arm64 Linux build machine
                 Utilities.setMachineAffinity(job, os, 'arm64-cross-latest')
             } else {
-                // Arm64 Test Machines
-                if (options['large_pages'] == false) {
-                    Utilities.setMachineAffinity(job, os, 'arm64-small-page-size')
-                } else {
+                // Arm64 Linux test machines
+                if ((options != null) && (options['large_pages'] == true)) {
                     Utilities.setMachineAffinity(job, os, 'arm64-huge-page-size')
+                } else {
+                    Utilities.setMachineAffinity(job, os, 'arm64-small-page-size')
+                }
+            }
+        }
+        else if (architecture == 'armem') {
+            // arm emulator (Ubuntu/Ubuntu16.04/Tizen). Build and test on same machine,
+            // using Docker.
+            Utilities.setMachineAffinity(job, 'Ubuntu', 'arm-cross-latest')
+        }
+        else {
+            // arm Ubuntu on hardware.
+            assert (architecture == 'arm') && (os == 'Ubuntu')
+            def isFlow  = (options != null) && (options['is_flow_job'] == true)
+            def isBuild = (options != null) && (options['is_build_job'] == true)
+            if (isFlow || isBuild) {
+                // arm Ubuntu build machine. Build uses docker, so the actual host OS is not
+                // very important. Therefore, use latest or auto. Flow jobs don't need to use
+                // arm hardware.
+                Utilities.setMachineAffinity(job, 'Ubuntu16.04', 'latest-or-auto')
+            } else {
+                // arm Ubuntu test machine
+                // There is no tag (like, e.g., "arm-latest") for this, so don't call
+                // Utilities.setMachineAffinity. Just add the machine affinity
+                // manually. We specify the Helix queue name here.
+                job.with {
+                    label('ubuntu.1404.arm32.open')
                 }
             }
         }
     }
 }
 
+// setJobMachineAffinity: compute the machine affinity options for a job,
+// then set the job with those affinity options.
+def static setJobMachineAffinity(def architecture, def os, def isBuildJob, def isTestJob, def isFlowJob, def job)
+{
+    assert (isBuildJob  && !isTestJob && !isFlowJob) ||
+           (!isBuildJob && isTestJob  && !isFlowJob) ||
+           (!isBuildJob && !isTestJob && isFlowJob)
+
+    def affinityOptions = null
+    def affinityArchitecture = architecture
+
+    if (os == "Windows_NT") {
+        if (architecture in Constants.armWindowsCrossArchitectureList) {
+            if (isBuildJob) {
+                affinityOptions = [ "use_arm64_build_machine" : true ]
+            } else if (isTestJob) {
+                affinityOptions = [ "use_arm64_build_machine" : false ]
+            } else if (isFlowJob) {
+                // For the flow jobs set the machine affinity as x64
+                affinityArchitecture = 'x64'
+            }
+        }
+    }
+    else {
+        if (architecture == 'arm64') {
+            if (isBuildJob) {
+                affinityOptions = ['is_build_only': true]
+            } else if (isTestJob) {
+                affinityOptions = [ "large_pages" : false ]
+            }
+        }
+        else if (architecture == 'arm') {
+            if (isBuildJob) {
+                affinityOptions = ['is_build_job': true]
+            } else if (isFlowJob) {
+                affinityOptions = ['is_flow_job': true]
+            }
+        }
+    }
+
+    setMachineAffinity(job, os, affinityArchitecture, affinityOptions)
+}
+
 def static isGCStressRelatedTesting(def scenario) {
     // The 'r2r_gcstress15' scenario is a basic scenario.
     // Detect it and make it a GCStress related.
@@ -599,7 +741,7 @@ def static isArmWindowsScenario(def scenario) {
 
 def static isValidPrTriggeredInnerLoopJob(os, architecture, configuration, isBuildOnly) {
     if (isBuildOnly == true) {
-        os = 'Windows_NT_buildOnly'
+        os = 'Windows_NT_BuildOnly'
     }
 
     def validOsPrTriggerArchConfigs = Constants.prTriggeredValidInnerLoopCombos[os]
@@ -655,20 +797,25 @@ def static setJobTimeout(newJob, isPR, architecture, configuration, scenario, is
         else if (isGcReliabilityFramework(scenario)) {
             timeout = 1440
         }
-        else if (architecture == 'arm' || architecture == 'armlb' || architecture == 'arm64') {
+        else if (architecture == 'armlb' || architecture == 'armem' || architecture == 'arm64') {
             timeout = 240
         }
+
+        if (architecture == 'arm') {
+            // ARM32 machines are particularly slow.
+            timeout += 120
+        }
     }
 
     if (configuration == 'Debug') {
         // Debug runs can be very slow. Add an hour.
         timeout += 60
     }
-       
-       if (architecture == 'x86_arm_altjit' || architecture == 'x64_arm64_altjit') {
-               // AltJit runs compile all methods twice.
-               timeout *= 2
-       }
+
+    if (architecture == 'x86_arm_altjit' || architecture == 'x64_arm64_altjit') {
+        // AltJit runs compile all methods twice.
+        timeout *= 2
+    }
 
     // If we've changed the timeout from the default, set it in the job.
 
@@ -802,8 +949,11 @@ def static isNeedDocker(def architecture, def os, def isBuild) {
         if (architecture == 'x86' && os == 'Ubuntu') {
             return true
         }
+        else if (architecture == 'armem') {
+            return true
+        }
         else if (architecture == 'arm') {
-            if (os == 'Ubuntu' || os == 'Ubuntu16.04' || os == 'Tizen') {
+            if (os == 'Ubuntu') {
                 return true
             }
         }
@@ -822,7 +972,7 @@ def static getDockerImageName(def architecture, def os, def isBuild) {
         if (architecture == 'x86' && os == 'Ubuntu') {
             return "hseok82/dotnet-buildtools-prereqs:ubuntu-16.04-crossx86-ef0ac75-20175511035548"
         }
-        else if (architecture == 'arm') {
+        else if (architecture == 'armem') {
             if (os == 'Ubuntu') {
                 return "microsoft/dotnet-buildtools-prereqs:ubuntu-14.04-cross-0cd4667-20172211042239"
             }
@@ -833,6 +983,11 @@ def static getDockerImageName(def architecture, def os, def isBuild) {
                 return "hqueue/dotnetcore:ubuntu1404_cross_prereqs_v4-tizen_rootfs"
             }
         }
+        else if (architecture == 'arm') {
+            if (os == 'Ubuntu') {
+                return "microsoft/dotnet-buildtools-prereqs:ubuntu-14.04-cross-0cd4667-20170319080304"
+            }
+        }
     }
     else {
         if (architecture == 'x86' && os == 'Ubuntu') {
@@ -843,6 +998,22 @@ def static getDockerImageName(def architecture, def os, def isBuild) {
     assert false
 }
 
+
+// We have a limited amount of some hardware. For these, scale back the periodic testing we do.
+def static jobRequiresLimitedHardware(def architecture, def os) {
+    if (((architecture == 'arm64') || (architecture == 'arm') || (architecture == 'armlb')) && (os == 'Windows_NT')) {
+        // These test jobs require ARM64 hardware
+        return true
+    }
+    else if ((architecture == 'arm') && (os == 'Ubuntu')) {
+        // These test jobs require Linux/arm32 hardware
+        return true
+    }
+    else {
+        return false
+    }
+}
+
 // Calculates the name of the build job based on some typical parameters.
 //
 def static getJobName(def configuration, def architecture, def os, def scenario, def isBuildOnly) {
@@ -879,7 +1050,7 @@ def static getJobName(def configuration, def architecture, def os, def scenario,
                 baseName = architecture.toLowerCase() + '_' + configuration.toLowerCase() + '_' + "small_page_size"
             }
             break
-        case 'arm':
+        case 'armem':
             // These are cross builds
             if (os == 'Tizen') {
                 // ABI: softfp
@@ -890,6 +1061,7 @@ def static getJobName(def configuration, def architecture, def os, def scenario,
             }
             break
         case 'armlb':
+        case 'arm':
             baseName = architecture.toLowerCase() + '_cross_' + configuration.toLowerCase() + '_' + os.toLowerCase()
             break
         case 'x86':
@@ -933,6 +1105,19 @@ def static addNonPRTriggers(def job, def branch, def isPR, def architecture, def
                     }
                     break
                 case 'arm':
+                    if (os == 'Windows_NT') {
+                        addGithubPushTriggerHelper(job)
+                    }
+                    else {
+                        // Currently no push triggers, with limited arm Linux hardware.
+                        // TODO: If we have enough machine capacity, add some arm Linux push triggers.
+                        assert os == 'Ubuntu'
+                        if (isFlowJob) {
+                            addPeriodicTriggerHelper(job, '@daily')
+                        }
+                    }
+                    break
+                case 'armem':
                 case 'armlb':
                 case 'x86_arm_altjit':
                 case 'x64_arm64_altjit':
@@ -1107,35 +1292,34 @@ def static addNonPRTriggers(def job, def branch, def isPR, def architecture, def
         case 'corefx_jitstressregs0x80':
         case 'corefx_jitstressregs0x1000':
         case 'zapdisable':
-            if (os != 'CentOS7.1' && !(os in bidailyCrossList)) {
-                assert (os == 'Windows_NT') || (os in Constants.crossList)
-                if ((architecture == 'arm64') || (architecture == 'arm') || (architecture == 'armlb')) {
-                    if (os == 'Windows_NT') {
-                        // We don't have enough ARM64 machines to run these more frequently than weekly.
-                        addPeriodicTriggerHelper(job, '@weekly')
-                    }
-                }
-                else {
-                    addPeriodicTriggerHelper(job, '@daily')
-                }
+            if (os == 'CentOS7.1') {
+                break
+            }
+            if (os in bidailyCrossList) {
+                break
+            }
+            assert (os == 'Windows_NT') || (os in Constants.crossList)
+            if (jobRequiresLimitedHardware(architecture, os)) {
+                addPeriodicTriggerHelper(job, '@weekly')
+            }
+            else {
+                addPeriodicTriggerHelper(job, '@daily')
             }
             break
         case 'heapverify1':
         case 'gcstress0x3':
-            if (os != 'CentOS7.1' && !(os in bidailyCrossList)) {
-                assert (os == 'Windows_NT') || (os in Constants.crossList)
-                if ((architecture == 'arm64') || (architecture == 'arm') || (architecture == 'armlb')) {
-                    if (os == 'Windows_NT') {
-                        // We don't have enough ARM64 machines to run these more frequently than weekly.
-                        addPeriodicTriggerHelper(job, '@weekly')
-                    }
-                    // TODO: Add once external email sending is available again
-                    // addEmailPublisher(job, 'dotnetonarm64@microsoft.com')
-                }
-                else {
-                    addPeriodicTriggerHelper(job, '@weekly')
-                }
+            if (os == 'CentOS7.1') {
+                break
+            }
+            if (os in bidailyCrossList) {
+                break
+            }
+            if ((architecture == 'arm64') && (os != 'Windows_NT')) {
+                // TODO: should we have cron jobs for arm64 Linux GCStress?
+                break
             }
+            assert (os == 'Windows_NT') || (os in Constants.crossList)
+            addPeriodicTriggerHelper(job, '@weekly')
             break
         case 'gcstress0xc':
         case 'gcstress0xc_zapdisable':
@@ -1144,21 +1328,22 @@ def static addNonPRTriggers(def job, def branch, def isPR, def architecture, def
         case 'gcstress0xc_jitstress1':
         case 'gcstress0xc_jitstress2':
         case 'gcstress0xc_minopts_heapverify1':
-            // GCStress=C is currently not supported on OS X
-            if (os != 'CentOS7.1' && os != 'OSX10.12' && !(os in bidailyCrossList)) {
-                assert (os == 'Windows_NT') || (os in Constants.crossList)
-                if ((architecture == 'arm64') || (architecture == 'arm') || (architecture == 'armlb')) {
-                    if (os == 'Windows_NT') {
-                        // We don't have enough ARM64 machines to run these more frequently than weekly.
-                        addPeriodicTriggerHelper(job, '@weekly')
-                    }
-                    // TODO: Add once external email sending is available again
-                    // addEmailPublisher(job, 'dotnetonarm64@microsoft.com')
-                }
-                else {
-                    addPeriodicTriggerHelper(job, '@weekly')
-                }
+            if (os == 'CentOS7.1') {
+                break
+            }
+            if (os == 'OSX10.12') {
+                // GCStress=C is currently not supported on OS X
+                break
+            }
+            if (os in bidailyCrossList) {
+                break
             }
+            if ((architecture == 'arm64') && (os != 'Windows_NT')) {
+                // TODO: should we have cron jobs for arm64 Linux GCStress?
+                break
+            }
+            assert (os == 'Windows_NT') || (os in Constants.crossList)
+            addPeriodicTriggerHelper(job, '@weekly')
             break
 
         case 'illink':
@@ -1486,42 +1671,27 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
             }
 
             break
-
         // editor brace matching: }
-        case 'armlb':
-        case 'arm': // editor brace matching: {
+
+        case 'armem': // editor brace matching: {
+            job.with {
+                publishers {
+                    azureVMAgentPostBuildAction {
+                        agentPostBuildAction('Delete agent if the build was not successful (when idle).')
+                    }
+                }
+            }
+
             switch (os) {
                 case 'Ubuntu':
                 case 'Ubuntu16.04':
-                    if (architecture == 'armlb') { // No arm legacy backend testing for Ubuntu
-                        break
-                    }
-
                     assert scenario != 'innerloop'
-                    job.with {
-                        publishers {
-                            azureVMAgentPostBuildAction {
-                                agentPostBuildAction('Delete agent if the build was not successful (when idle).')
-                            }
-                        }
-                    }
                     Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} Cross ${configuration} Build",
                             "(?i).*test\\W+${os}\\W+${architecture}\\W+Cross\\W+${configuration}\\W+Build.*")
                     break
 
                 case 'Tizen':
-                    if (architecture == 'armlb') {  // No arm legacy backend testing for Tizen armel
-                        break
-                    }
-
                     architecture = 'armel'
-                    job.with {
-                        publishers {
-                            azureVMAgentPostBuildAction {
-                                agentPostBuildAction('Delete agent if the build was not successful (when idle).')
-                            }
-                        }
-                    }
 
                     if (scenario == 'innerloop') {
                         if (configuration == 'Checked') {
@@ -1533,40 +1703,62 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
                             "(?i).*test\\W+${os}\\W+${architecture}\\W+Cross\\W+${configuration}\\W+Build.*")
                     }
                     break
+            }
 
-                case 'Windows_NT':
-                    if (architecture == "armlb") {
-                        // Disable armlb windows jobs
-                        break
-                    }
+            break
+        // editor brace matching: }
 
-                    // Triggers on the non-flow jobs aren't necessary here
-                    if (!isFlowJob) {
+        case 'armlb':
+        case 'arm': // editor brace matching: {
+
+            // Triggers on the non-flow jobs aren't necessary
+            if (!isFlowJob) {
+                break
+            }
+
+            // Set up a private trigger
+            def contextString = "${os} ${architecture} Cross ${configuration}"
+            def triggerString = "(?i).*test\\W+${os}\\W+${architecture}\\W+Cross\\W+${configuration}"
+            if (scenario == 'innerloop') {
+                contextString += " Innerloop"
+                triggerString += "\\W+Innerloop"
+            }
+            else {
+                contextString += " ${scenario}"
+                triggerString += "\\W+${scenario}"
+            }
+
+            if (configuration == 'Debug') {
+                contextString += " Build"
+                triggerString += "\\W+Build"
+            } else {
+                contextString += " Build and Test"
+                triggerString += "\\W+Build and Test"
+            }
+
+            triggerString += ".*"
+
+            switch (os) {
+                case 'Ubuntu':
+                    if (architecture == 'armlb') { // No arm legacy backend testing for Ubuntu
                         break
                     }
 
-                    // Set up a private trigger
-                    def contextString = "${os} ${architecture} Cross ${configuration}"
-                    def triggerString = "(?i).*test\\W+${os}\\W+${architecture}\\W+Cross\\W+${configuration}"
                     if (scenario == 'innerloop') {
-                        contextString += " Innerloop"
-                        triggerString += "\\W+Innerloop"
+                        if (configuration == 'Checked') {
+                            Utilities.addGithubPRTriggerForBranch(job, branch, contextString)
+                        }
                     }
                     else {
-                        contextString += " ${scenario}"
-                        triggerString += "\\W+${scenario}"
+                        Utilities.addGithubPRTriggerForBranch(job, branch, contextString, triggerString)
                     }
+                    break
 
-                    if (configuration == 'Debug') {
-                        contextString += " Build"
-                        triggerString += "\\W+Build"
-                    } else {
-                        contextString += " Build and Test"
-                        triggerString += "\\W+Build and Test"
+                case 'Windows_NT':
+                    if (architecture == "armlb") {
+                        // Disable armlb windows jobs
+                        break
                     }
-
-                    triggerString += ".*"
-
                     switch (scenario) {
                         case 'innerloop':
                             // Only Checked is an innerloop trigger.
@@ -1781,7 +1973,7 @@ def static addTriggers(def job, def branch, def isPR, def architecture, def os,
 }
 
 def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR, def architecture, def configuration, def os, def isBuildOnly) {
-    def buildCommands = [];
+    def buildCommands = []
     def osGroup = getOSGroup(os)
     def lowerConfiguration = configuration.toLowerCase()
 
@@ -1790,9 +1982,7 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
         priority = '0'
     }
 
-    setJobTimeout(newJob, isPR, architecture, configuration, scenario, isBuildOnly)
-
-    def enableCorefxTesting = isCoreFxScenario(scenario)
+    def doCoreFxTesting = isCoreFxScenario(scenario)
 
     // Calculate the build steps, archival, and xunit results
     switch (os) {
@@ -1821,7 +2011,7 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
                         buildCommands += "tests\\scripts\\build_illink.cmd clone ${arch}"
                     }
 
-                    // If it is a release build for windows, ensure PGO is used, else fail the build
+                    // If it is a release build for Windows, ensure PGO is used, else fail the build.
                     if ((lowerConfiguration == 'release') &&
                         (scenario in Constants.basicScenarios) &&
                         (architecture != 'x86_arm_altjit') &&
@@ -1830,7 +2020,7 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
                         buildOpts += ' -enforcepgo'
                     }
 
-                    if (enableCorefxTesting) {
+                    if (doCoreFxTesting) {
                         buildOpts += ' skiptests';
                     } else {
                         buildOpts += " -priority=${priority}"
@@ -1928,7 +2118,7 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
 
                         runtestArguments = "${lowerConfiguration} ${arch} ${testOpts}"
 
-                        if (enableCorefxTesting) {
+                        if (doCoreFxTesting) {
                             def workspaceRelativeFxRoot = "_/fx"
                             def absoluteFxRoot = "%WORKSPACE%\\_\\fx"
 
@@ -1949,15 +2139,15 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
                         else {
                             buildCommands += "tests\\runtest.cmd ${runtestArguments}"
                         }
-                    }
+                    } // end if (!isBuildOnly)
 
-                    if (!enableCorefxTesting) {
+                    if (!doCoreFxTesting) {
                         // Run the rest of the build
                         // Build the mscorlib for the other OS's
                         buildCommands += "build.cmd ${lowerConfiguration} ${arch} linuxmscorlib"
                         buildCommands += "build.cmd ${lowerConfiguration} ${arch} osxmscorlib"
                        
-                        if (arch == "x64") {
+                        if (arch == 'x64') {
                             buildCommands += "build.cmd ${lowerConfiguration} arm64 linuxmscorlib"
                         }
 
@@ -1988,9 +2178,6 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
                 case 'arm':
                     assert isArmWindowsScenario(scenario)
 
-                    def machineAffinityOptions = ['use_arm64_build_machine' : true]
-                    setMachineAffinity(newJob, os, architecture, machineAffinityOptions)
-
                     def buildArchitecture = 'arm'
 
                     def buildOpts = ''
@@ -2001,7 +2188,7 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
                         buildOpts += ' -crossgenaltjit legacyjit.dll'
                     }
 
-                    if (enableCorefxTesting) {
+                    if (doCoreFxTesting) {
                         // We shouldn't need to build the tests. However, run-corefx-tests.py currently depends on having the restored corefx
                         // package available, to determine the correct corefx version git commit hash, and we need to build the tests before
                         // running "tests\\runtest.cmd GenerateLayoutOnly". So build the pri-0 tests to make this happen.
@@ -2015,7 +2202,7 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
                     // This is now a build only job. Do not run tests. Use the flow job.
                     buildCommands += "set __TestIntermediateDir=int&&build.cmd ${lowerConfiguration} ${buildArchitecture} ${buildOpts}"
 
-                    if (enableCorefxTesting) {
+                    if (doCoreFxTesting) {
                         assert isBuildOnly
                         assert architecture == 'arm'
 
@@ -2056,9 +2243,6 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
                 case 'arm64':
                     assert isArmWindowsScenario(scenario)
 
-                    def machineAffinityOptions = ['use_arm64_build_machine' : true]
-                    setMachineAffinity(newJob, os, architecture, machineAffinityOptions)
-
                     // This is now a build only job. Do not run tests. Use the flow job.
                     buildCommands += "set __TestIntermediateDir=int&&build.cmd ${lowerConfiguration} ${architecture} toolset_dir C:\\ats2 -priority=${priority}"
 
@@ -2075,7 +2259,7 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
                     break
             }
             break
-        // editor brace matching: }
+        // end case 'Windows_NT'; editor brace matching: }
         case 'Ubuntu':
         case 'Ubuntu16.04':
         case 'Ubuntu16.10':
@@ -2110,7 +2294,7 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
                         buildCommands += "./tests/scripts/build_illink.sh --clone --arch=${architecture}"
                     }
 
-                    if (!enableCorefxTesting) {
+                    if (!doCoreFxTesting) {
                         // We run pal tests on all OS but generate mscorlib (and thus, nuget packages)
                         // only on supported OS platforms.
                         def bootstrapRid = Utilities.getBoostrapPublishRid(os)
@@ -2153,7 +2337,7 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
                     }
                     break
                 case 'arm64':
-                    if (!enableCorefxTesting) {
+                    if (!doCoreFxTesting) {
                         buildCommands += "ROOTFS_DIR=/opt/arm64-xenial-rootfs ./build.sh verbose ${lowerConfiguration} ${architecture} cross clang3.8"
                         
                         // HACK -- Arm64 does not have corefx jobs yet.
@@ -2166,19 +2350,19 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
                         Utilities.addArchival(newJob, "bin/Product/**,bin/obj/*/tests/**/*.dylib,bin/obj/*/tests/**/*.so", "bin/Product/**/.nuget/**")
                     }
                     break
-                case 'arm':
-                    // Cross builds for ARM runs on Ubuntu, Ubuntu16.04 and Tizen currently
+                case 'armem':
+                    // Emulator cross builds for ARM runs on Ubuntu, Ubuntu16.04 and Tizen currently
                     assert (os == 'Ubuntu') || (os == 'Ubuntu16.04') || (os == 'Tizen')
 
                     // default values for Ubuntu
-                    def arm_abi="arm"
-                    def linuxCodeName="trusty"
+                    def arm_abi = "arm"
+                    def linuxCodeName = "trusty"
                     if (os == 'Ubuntu16.04') {
-                        linuxCodeName="xenial"
+                        linuxCodeName = "xenial"
                     }
                     else if (os == 'Tizen') {
-                        arm_abi="armel"
-                        linuxCodeName="tizen"
+                        arm_abi = "armel"
+                        linuxCodeName = "tizen"
                     }
 
                     // Unzip the Windows test binaries first. Exit with 0
@@ -2209,6 +2393,40 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
                     // Basic archiving of the build, no pal tests
                     Utilities.addArchival(newJob, "bin/Product/**,bin/obj/*/tests/**/*.dylib,bin/obj/*/tests/**/*.so", "bin/Product/**/.nuget/**")
                     break
+                case 'arm':
+                    // Non-Windows ARM cross builds on hardware run on Ubuntu only
+                    assert (os == 'Ubuntu')
+
+                    // Add some useful information to the log file. Ignore return codes.
+                    buildCommands += "uname -a || true"
+
+                    // Cross build the Ubuntu/arm product using docker with a docker image that contains the correct
+                    // Ubuntu cross-compilation toolset (running on a Ubuntu x64 host).
+
+                    def dockerImage = getDockerImageName(architecture, os, true)
+                    def dockerCmd = "docker run -i --rm -v \${WORKSPACE}:\${WORKSPACE} -w \${WORKSPACE} -e ROOTFS_DIR=/crossrootfs/arm ${dockerImage} "
+
+                    buildCommands += "${dockerCmd}\${WORKSPACE}/build.sh ${lowerConfiguration} ${architecture} cross"
+
+                    // Then, using the same docker image, generate the CORE_ROOT layout using build-test.sh to
+                    // download the appropriate CoreFX packages.
+                    // Note that docker should not be necessary here, for the "generatelayoutonly" case, but we use it
+                    // just to be consistent with the "build.sh" case -- so both are run with the same environment.
+
+                    buildCommands += "${dockerCmd}\${WORKSPACE}/build-test.sh ${lowerConfiguration} ${architecture} cross generatelayoutonly"
+
+                    // ZIP up for the test job (created in the flow job code):
+                    // (1) the built CORE_ROOT, /home/user/coreclr/bin/tests/Linux.arm.Checked/Tests/Core_Root,
+                    //     used by runtest.sh as the "--coreOverlayDir" argument.
+                    // (2) the native parts of the test build: /home/user/coreclr/bin/obj/Linux.arm.Checked/tests,
+                    //     used by runtest.sh as the "--testNativeBinDir" argument.
+
+                    // These commands are assumed to be run from the root of the workspace.
+                    buildCommands += "zip -r coreroot.${lowerConfiguration}.zip ./bin/tests/Linux.arm.${configuration}/Tests/Core_Root"
+                    buildCommands += "zip -r testnativebin.${lowerConfiguration}.zip ./bin/obj/Linux.arm.${configuration}/tests"
+
+                    Utilities.addArchival(newJob, "coreroot.${lowerConfiguration}.zip,testnativebin.${lowerConfiguration}.zip", "")
+                    break
                 default:
                     println("Unknown architecture: ${architecture}");
                     assert false
@@ -2225,225 +2443,253 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
     return buildCommands
 }
 
-Constants.allScenarios.each { scenario ->
-    [true, false].each { isPR ->
-        Constants.architectureList.each { architecture ->
-            Constants.configurationList.each { configuration ->
-                Constants.osList.each { os ->
-                    // If the OS is Windows_NT_BuildOnly, set the isBuildOnly flag to true
-                    // and reset the os to Windows_NT
-                    def isBuildOnly = false
-                    if (os == 'Windows_NT_BuildOnly') {
-                        isBuildOnly = true
-                        os = 'Windows_NT'
-                    }
-
-                    // Tizen is only supported for arm architecture
-                    if (os == 'Tizen' && architecture != 'arm') {
-                        return
-                    }
-
-                    // Skip totally unimplemented (in CI) configurations.
-                    switch (architecture) {
-                        case 'arm64':
-                            if (os == 'Ubuntu16.04') {
-                                os = 'Ubuntu'
-                            }
-
-                            // Windows and Ubuntu only
-                            if ((os != 'Windows_NT' && os != 'Ubuntu') || isBuildOnly) {
-                                return
-                            }
-                            break
-                        case 'arm':
-                            if ((os != 'Ubuntu') && (os != 'Ubuntu16.04') && (os != 'Tizen') && (os != 'Windows_NT')) {
-                                return
-                            }
-                            break
-                        case 'armlb':
-                            // Do not create armlb jobs
-                            return
-                            break
-                        case 'x86':
-                            if ((os != 'Ubuntu') && (os != 'Windows_NT')) {
-                                return
-                            }
-                            break
-                        case 'x86_arm_altjit':
-                        case 'x64_arm64_altjit':
-                            if (os != 'Windows_NT') {
-                                return
-                            }
-                            break
-                        case 'x64':
-                            // Everything implemented
-                            break
-                        default:
-                            println("Unknown architecture: ${architecture}")
-                            assert false
-                            break
-                    }
-
-                    // Skip scenarios (blanket skipping for jit stress modes, which are good most everywhere
-                    // with checked builds)
-                    if (isJitStressScenario(scenario)) {
-                        if (configuration != 'Checked') {
-                            return
-                        }
-
-                        // Since these are just execution time differences,
-                        // skip platforms that don't execute the tests here (Windows_NT only)
-                        def isEnabledOS = (os == 'Windows_NT') || (os == 'Ubuntu' && isCoreFxScenario(scenario))
-                        if (!isEnabledOS) {
-                            return
-                        }
+// Determine if we should generate a job for the given parameters. This is for non-flow jobs: either build and test, or build only.
+// Returns true if the job should be generated.
+def static shouldGenerateJob(def scenario, def isPR, def architecture, def configuration, def os, def isBuildOnly)
+{
+    // The "innerloop" (Pri-0 testing) scenario is only available as PR triggered.
+    // All other scenarios do Pri-1 testing.
+    if (scenario == 'innerloop' && !isPR) {
+        return false
+    }
 
-                        switch (architecture) {
-                            case 'x64':
-                            case 'x86':
-                            case 'x86_arm_altjit':
-                            case 'x64_arm64_altjit':
-                                // x86 ubuntu: default only
-                                if ((os == 'Ubuntu') && (architecture == 'x86')) {
-                                    return
-                                }
-                                if (isBuildOnly) {
-                                    return
-                                }
-                                break
+    // Tizen is only supported for armem architecture
+    if (os == 'Tizen' && architecture != 'armem') {
+        return false
+    }
 
-                            case 'arm':
-                                // We use build only jobs for Windows arm cross-compilation corefx testing, so we need to generate builds for that.
-                                if (!isBuildOnly || !isCoreFxScenario(scenario)) {
-                                    return
-                                }
-                                break
+    // Filter based on architecture.
 
-                            default:
-                                // arm64, armlb: stress is handled through flow jobs.
-                                return
-                        }
-                    }
-                    else if (isR2RScenario(scenario)) {
-                        if (os != 'Windows_NT') {
-                            return
-                        }
-                        // Stress scenarios only run with Checked builds, not Release (they would work with Debug, but be slow).
-                        if ((configuration != 'Checked') && isR2RStressScenario(scenario)) {
-                            return
-                        }
-                    }
-                    else {
-                        // Skip scenarios
-                        switch (scenario) {
-                            case 'ilrt':
-                                // The ilrt build isn't necessary except for Windows_NT2003.  Non-Windows NT uses
-                                // the default scenario build
-                                if (os != 'Windows_NT') {
-                                    return
-                                }
-                                // Only x64 for now
-                                if (architecture != 'x64') {
-                                    return
-                                }
-                                // Release only
-                                if (configuration != 'Release') {
-                                    return
-                                }
-                                break
-                            case 'jitdiff':
-                                if (os != 'Windows_NT' && os != 'Ubuntu' && os != 'OSX10.12') {
-                                    return
-                                }
-                                if (architecture != 'x64') {
-                                    return
-                                }
-                                if (configuration != 'Checked') {
-                                    return
-                                }
-                                break
-                            case 'longgc':
-                            case 'gcsimulator':
-                                if (os != 'Windows_NT' && os != 'Ubuntu' && os != 'OSX10.12') {
-                                    return
-                                }
-                                if (architecture != 'x64') {
-                                    return
-                                }
-                                if (configuration != 'Release') {
-                                    return
-                                }
-                                break
-                            case 'gc_reliability_framework':
-                            case 'standalone_gc':
-                                if (os != 'Windows_NT' && os != 'Ubuntu' && os != 'OSX10.12') {
-                                    return
-                                }
+    switch (architecture) {
+        case 'arm64':
+        case 'arm':
+            if ((os != 'Windows_NT') && (os != 'Ubuntu')) {
+                return false
+            }
+            break
+        case 'armem':
+            if ((os != 'Ubuntu') && (os != 'Ubuntu16.04') && (os != 'Tizen')) {
+                return false
+            }
+            break
+        case 'armlb':
+            // Do not create armlb jobs
+            return false
+        case 'x86_arm_altjit':
+        case 'x64_arm64_altjit':
+            if (os != 'Windows_NT') {
+                return false
+            }
+            break
+        case 'x86':
+            if ((os != 'Windows_NT') && (os != 'Ubuntu')) {
+                return false
+            }
+            break
+        case 'x64':
+            // Everything implemented
+            break
+        default:
+            println("Unknown architecture: ${architecture}")
+            assert false
+            break
+    }
 
-                                if (architecture != 'x64') {
-                                    return
-                                }
+    // Which (Windows) build only jobs are required?
 
-                                if (configuration != 'Release' && configuration != 'Checked') {
-                                    return
-                                }
-                                break
-                            // We only run Windows and Ubuntu x64 Checked for formatting right now
-                            case 'formatting':
-                                if (os != 'Windows_NT' && os != 'Ubuntu') {
-                                    return
-                                }
-                                if (architecture != 'x64') {
-                                    return
-                                }
-                                if (configuration != 'Checked') {
-                                    return
-                                }
-                                if (isBuildOnly) {
-                                    return
-                                }
-                                break
-                            case 'illink':
-                                if (os != 'Windows_NT' && (os != 'Ubuntu' || architecture != 'x64')) {
-                                    return
-                                }
-                                if (architecture != 'x64' && architecture != 'x86') {
-                                    return
-                                }
-                                if (isBuildOnly) {
-                                    return
-                                }
-                                break
-                            case 'normal':
-                                // Nothing skipped
-                                break
-                            case 'innerloop':
-                                if (!isValidPrTriggeredInnerLoopJob(os, architecture, configuration, isBuildOnly)) {
-                                    return
-                                }
-                                break
-                            default:
-                                println("Unknown scenario: ${scenario}")
-                                assert false
-                                break
-                        }
+    def isNormalOrInnerloop = (scenario == 'innerloop' || scenario == 'normal')
+
+    if (isBuildOnly) {
+        switch (architecture) {
+            case 'arm':
+                // We use build only jobs for Windows arm cross-compilation corefx testing, so we need to generate builds for that.
+                if (!isCoreFxScenario(scenario)) {
+                    return false
+                }
+                break
+            case 'x64':
+            case 'x86':
+                if (!isNormalOrInnerloop) {
+                    return false
+                }
+                break
+            default:
+                return false
+        }
+    }
+
+    // Filter based on scenario.
+
+    if (isJitStressScenario(scenario)) {
+        if (configuration != 'Checked') {
+            return false
+        }
+
+        def isEnabledOS = (os == 'Windows_NT') || (os == 'Ubuntu' && architecture == 'arm') || (os == 'Ubuntu' && isCoreFxScenario(scenario))
+        if (!isEnabledOS) {
+            return false
+        }
+
+        switch (architecture) {
+            case 'x64':
+            case 'x86_arm_altjit':
+            case 'x64_arm64_altjit':
+                break
+
+            case 'x86':
+                // x86 ubuntu: no stress modes
+                if (os == 'Ubuntu') {
+                    return false
+                }
+                break
+
+            case 'arm':
+                // We use build only jobs for Windows arm cross-compilation corefx testing, so we need to generate builds for that.
+                if (! (isBuildOnly && isCoreFxScenario(scenario)) ) {
+                    return false
+                }
+                break
+
+            default:
+                // arm64, armlb: stress is handled through flow jobs.
+                // armem: no stress jobs for ARM emulator.
+                return false
+        }
+    }
+    else if (isR2RScenario(scenario)) {
+        if (os != 'Windows_NT') {
+            return false
+        }
+        // Stress scenarios only run with Checked builds, not Release (they would work with Debug, but be slow).
+        if ((configuration != 'Checked') && isR2RStressScenario(scenario)) {
+            return false
+        }
+    }
+    else {
+        // Skip scenarios
+        switch (scenario) {
+            case 'ilrt':
+                // The ilrt build isn't necessary except for Windows_NT2003.  Non-Windows NT uses
+                // the default scenario build
+                if (os != 'Windows_NT') {
+                    return false
+                }
+                // Only x64 for now
+                if (architecture != 'x64') {
+                    return false
+                }
+                // Release only
+                if (configuration != 'Release') {
+                    return false
+                }
+                break
+            case 'jitdiff':
+                if (os != 'Windows_NT' && os != 'Ubuntu' && os != 'OSX10.12') {
+                    return false
+                }
+                if (architecture != 'x64') {
+                    return false
+                }
+                if (configuration != 'Checked') {
+                    return false
+                }
+                break
+            case 'longgc':
+            case 'gcsimulator':
+                if (os != 'Windows_NT' && os != 'Ubuntu' && os != 'OSX10.12') {
+                    return false
+                }
+                if (architecture != 'x64') {
+                    return false
+                }
+                if (configuration != 'Release') {
+                    return false
+                }
+                break
+            case 'gc_reliability_framework':
+            case 'standalone_gc':
+                if (os != 'Windows_NT' && os != 'Ubuntu' && os != 'OSX10.12') {
+                    return false
+                }
+
+                if (architecture != 'x64') {
+                    return false
+                }
+
+                if (configuration != 'Release' && configuration != 'Checked') {
+                    return false
+                }
+                break
+            // We only run Windows and Ubuntu x64 Checked for formatting right now
+            case 'formatting':
+                if (os != 'Windows_NT' && os != 'Ubuntu') {
+                    return false
+                }
+                if (architecture != 'x64') {
+                    return false
+                }
+                if (configuration != 'Checked') {
+                    return false
+                }
+                break
+            case 'illink':
+                if (os != 'Windows_NT' && (os != 'Ubuntu' || architecture != 'x64')) {
+                    return false
+                }
+                if (architecture != 'x64' && architecture != 'x86') {
+                    return false
+                }
+                break
+            case 'normal':
+                // Nothing skipped
+                break
+            case 'innerloop':
+                if (!isValidPrTriggeredInnerLoopJob(os, architecture, configuration, isBuildOnly)) {
+                    return false
+                }
+                break
+            default:
+                println("Unknown scenario: ${scenario}")
+                assert false
+                break
+        }
+    }
+
+    // For altjit, don't do any scenarios that don't change compilation. That is, scenarios that only change
+    // runtime behavior, not compile-time behavior, are not interesting.
+    switch (architecture) {
+        case 'x86_arm_altjit':
+        case 'x64_arm64_altjit':
+            if (isGCStressRelatedTesting(scenario)) {
+                return false
+            }
+            break
+        default:
+            break
+    }
+
+    // The job was not filtered out, so we should generate it!
+    return true
+}
+
+Constants.allScenarios.each { scenario ->
+    [true, false].each { isPR ->
+        Constants.architectureList.each { architecture ->
+            Constants.configurationList.each { configuration ->
+                Constants.osList.each { os ->
+                    // If the OS is Windows_NT_BuildOnly, set the isBuildOnly flag to true
+                    // and reset the os to Windows_NT
+                    def isBuildOnly = false
+                    if (os == 'Windows_NT_BuildOnly') {
+                        isBuildOnly = true
+                        os = 'Windows_NT'
                     }
 
-                    // For altjit, don't do any scenarios that don't change compilation. That is, scenarios that only change
-                    // runtime behavior, not compile-time behavior, are not interesting.
-                    switch (architecture) {
-                        case 'x86_arm_altjit':
-                        case 'x64_arm64_altjit':
-                            if (isGCStressRelatedTesting(scenario)) {
-                                return
-                            }
-                            break
-                        default:
-                            break
+                    if (!shouldGenerateJob(scenario, isPR, architecture, configuration, os, isBuildOnly)) {
+                        return
                     }
 
                     // Calculate names
-                    def lowerConfiguration = configuration.toLowerCase()
                     def jobName = getJobName(configuration, architecture, os, scenario, isBuildOnly)
                     def folderName = getJobFolder(scenario)
 
@@ -2451,23 +2697,59 @@ Constants.allScenarios.each { scenario ->
                     def newJob = job(Utilities.getFullJobName(project, jobName, isPR, folderName)) {}
                     addToViews(newJob, isPR, architecture, os)
 
-                    def machineAffinityOptions = null
-                    
-                    if (os != 'Windows_NT') {
-                        machineAffinityOptions = architecture == 'arm64' ? ['is_build_only': true] : null
-                    }
-                    else {
-                        machineAffinityOptions = (architecture == 'arm' || architecture == 'armlb' || architecture == 'arm64') ? ['use_arm64_build_machine': false] : null
-                    }
-
-                    setMachineAffinity(newJob, os, architecture, machineAffinityOptions)
+                    setJobMachineAffinity(architecture, os, true, false, false, newJob) // isBuildJob = true, isTestJob = false, isFlowJob = false
 
-                    // Add all the standard options
                     Utilities.standardJobSetup(newJob, project, isPR, "*/${branch}")
                     addTriggers(newJob, branch, isPR, architecture, os, configuration, scenario, false, isBuildOnly) // isFlowJob==false
+                    setJobTimeout(newJob, isPR, architecture, configuration, scenario, isBuildOnly)
+
+                    // Copy Windows build test binaries and corefx build artifacts for Linux cross build for armem.
+                    // We don't use a flow job for this, but we do depend on there being existing builds with these
+                    // artifacts produced.
+                    if (architecture == 'armem' && (os == 'Ubuntu' || os == 'Ubuntu16.04' || os == 'Tizen')) {
+                        // Define the Windows Tests and Corefx build job names
+                        def lowerConfiguration = configuration.toLowerCase()
+                        def WindowsTestsName = projectFolder + '/' +
+                                               Utilities.getFullJobName(project,
+                                                                        getJobName(lowerConfiguration, 'x64' , 'windows_nt', 'normal', true),
+                                                                        false)
+                        def corefxFolder = Utilities.getFolderName('dotnet/corefx') + '/' +
+                                           Utilities.getFolderName(branch)
+
+                        def arm_abi = 'arm'
+                        def corefx_os = 'linux'
+                        if (os == 'Tizen') {
+                            arm_abi = 'armel'
+                            corefx_os = 'tizen'
+                        }
+
+                        // Let's use release CoreFX to test checked CoreCLR,
+                        // because we do not generate checked CoreFX in CoreFX CI yet.
+                        def corefx_lowerConfiguration = lowerConfiguration
+                        if (lowerConfiguration == 'checked') {
+                            corefx_lowerConfiguration = 'release'
+                        }
+
+                        // Copy the Windows test binaries and the Corefx build binaries
+                        newJob.with {
+                            steps {
+                                copyArtifacts(WindowsTestsName) {
+                                    includePatterns('bin/tests/tests.zip')
+                                    buildSelector {
+                                        latestSuccessful(true)
+                                    }
+                                }
+                                copyArtifacts("${corefxFolder}/${corefx_os}_${arm_abi}_cross_${corefx_lowerConfiguration}") {
+                                    includePatterns('bin/build.tar.gz')
+                                    buildSelector {
+                                        latestSuccessful(true)
+                                    }
+                                }
+                            } // steps
+                        } // newJob.with
+                    }
 
                     def buildCommands = calculateBuildCommands(newJob, scenario, branch, isPR, architecture, configuration, os, isBuildOnly)
-                    def osGroup = getOSGroup(os)
 
                     newJob.with {
                         steps {
@@ -2477,52 +2759,11 @@ Constants.allScenarios.each { scenario ->
                                 }
                             }
                             else {
-                                // Setup corefx and Windows test binaries for Linux cross build for ubuntu-arm, ubuntu16.04-arm and tizen-armel
-                                if ( architecture == 'arm' && ( os == 'Ubuntu' || os == 'Ubuntu16.04' || os == 'Tizen')) {
-                                    // Cross build for ubuntu-arm, ubuntu16.04-arm and tizen-armel
-                                    // Define the Windows Tests and Corefx build job names
-                                    def WindowsTestsName = projectFolder + '/' +
-                                                           Utilities.getFullJobName(project,
-                                                                                    getJobName(lowerConfiguration, 'x64' , 'windows_nt', 'normal', true),
-                                                                                    false)
-                                    def corefxFolder = Utilities.getFolderName('dotnet/corefx') + '/' +
-                                                       Utilities.getFolderName(branch)
-
-                                    // Copy the Windows test binaries and the Corefx build binaries
-                                    copyArtifacts(WindowsTestsName) {
-                                        includePatterns('bin/tests/tests.zip')
-                                        buildSelector {
-                                            latestSuccessful(true)
-                                        }
-                                    }
-
-                                    def arm_abi = 'arm'
-                                    def corefx_os = 'linux'
-                                    if (os == 'Tizen') {
-                                        arm_abi = 'armel'
-                                        corefx_os = 'tizen'
-                                    }
-
-                                    // Let's use release CoreFX to test checked CoreCLR,
-                                    // because we do not generate checked CoreFX in CoreFX CI yet.
-                                    def corefx_lowerConfiguration = lowerConfiguration
-                                    if ( lowerConfiguration == 'checked' ) {
-                                        corefx_lowerConfiguration='release'
-                                    }
-
-                                    copyArtifacts("${corefxFolder}/${corefx_os}_${arm_abi}_cross_${corefx_lowerConfiguration}") {
-                                        includePatterns('bin/build.tar.gz')
-                                        buildSelector {
-                                            latestSuccessful(true)
-                                        }
-                                    }
-                                }
-
                                 buildCommands.each { buildCommand ->
                                     shell(buildCommand)
                                 }
                             }
-                        }
+                        } // steps
                     } // newJob.with
 
                 } // os
@@ -2531,685 +2772,787 @@ Constants.allScenarios.each { scenario ->
     } // isPR
 } // scenario
 
+// Create a Windows ARM/ARMLB/ARM64 test job that will be used by a flow job.
+// Returns the newly created job.
+def static CreateWindowsArmTestJob(def dslFactory, def project, def architecture, def os, def configuration, def scenario, def isPR, def inputCoreCLRBuildName)
+{
+    def osGroup = getOSGroup(os)
+    def jobName = getJobName(configuration, architecture, os, scenario, false) + "_tst"
 
-// Create jobs requiring flow jobs. This includes x64 non-Windows, arm64 Ubuntu, and arm/arm64/armlb Windows.
-Constants.allScenarios.each { scenario ->
-    def isNormalOrInnerloop = (scenario == 'innerloop' || scenario == 'normal')
+    def jobFolder = getJobFolder(scenario)
+    def newJob = dslFactory.job(Utilities.getFullJobName(project, jobName, isPR, jobFolder)) {
+        parameters {
+            stringParam('CORECLR_BUILD', '', "Build number to copy CoreCLR ${osGroup} binaries from")
+        }
 
-    [true, false].each { isPR ->
-        ['arm', 'armlb', 'x64', 'arm64', 'x86'].each { architecture ->
-            Constants.crossList.each { os ->
-                if (architecture == 'arm64') {
-                    if (os != "Ubuntu" && os != "Windows_NT") {
-                        return
-                    }
-                } else if (architecture == 'arm') {
-                    if (os != 'Windows_NT') {
-                        return
-                    }
-                } else if (architecture == 'armlb') {
-                  // Do not create armlb windows jobs.
-                  return  
-                } else if (architecture == 'x86') {
-                    if (os != "Ubuntu") {
-                        return
-                    }
-                }
+        steps {
+            // Set up the copies
 
-                def validWindowsNTCrossArches = ["arm", "armlb", "arm64"]
+            // Coreclr build we are trying to test
+            //
+            //  ** NOTE ** This will, correctly, overwrite the CORE_ROOT from the Windows test archive
 
-                if (os == "Windows_NT" && !(architecture in validWindowsNTCrossArches)) {
-                    return
+            copyArtifacts(inputCoreCLRBuildName) {
+                excludePatterns('**/testResults.xml', '**/*.ni.dll')
+                buildSelector {
+                    buildNumber('${CORECLR_BUILD}')
                 }
+            }
 
-                Constants.configurationList.each { configuration ->
+            if (isCoreFxScenario(scenario)) {
 
-                    // First, filter based on OS.
+                // Only arm supported for corefx testing now.
+                assert architecture == 'arm'
 
-                    if (os == 'Windows_NT') {
-                        if (!isArmWindowsScenario(scenario)) {
-                            return
-                        }
-                    }
-                    else {
-                        // Non-Windows
-                        if (architecture == 'arm64') {
-                            if (!(scenario in Constants.validLinuxArm64Scenarios)) {
-                                return
-                            }
-                        }
-                        else if (architecture == 'x86') {
-                            // Linux/x86 only want innerloop and default test
-                            if (!isNormalOrInnerloop) {
-                                return
-                            }
-                        }
-                    }
+                // Unzip CoreFx runtime
+                batchFile("powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::ExtractToDirectory('_\\fx\\fxruntime.zip', '_\\fx\\bin\\testhost\\netcoreapp-Windows_NT-Release-arm')")
 
-                    // For CentOS, we only want Checked/Release builds.
-                    if (os == 'CentOS7.1') {
-                        if (configuration != 'Checked' && configuration != 'Release') {
-                            return
-                        }
-                        if (!isNormalOrInnerloop && !isR2RScenario(scenario) && !isJitStressScenario(scenario)) {
-                            return
-                        }
-                    }
+                // Unzip CoreFx tests.
+                batchFile("powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::ExtractToDirectory('_\\fx\\fxtests.zip', '_\\fx\\bin\\tests')")
 
-                    // For RedHat and Debian, we only do Release builds.
-                    else if (os == 'RHEL7.2' || os == 'Debian8.4') {
-                        if (configuration != 'Release') {
-                            return
-                        }
-                        if (!isNormalOrInnerloop) {
-                            return
-                        }
-                    }
+                // Add the script to run the corefx tests
+                def corefx_runtime_path   = "%WORKSPACE%\\_\\fx\\bin\\testhost\\netcoreapp-Windows_NT-Release-arm"
+                def corefx_tests_dir      = "%WORKSPACE%\\_\\fx\\bin\\tests"
+                def corefx_exclusion_file = "%WORKSPACE%\\tests\\arm\\corefx_test_exclusions.txt"
+                batchFile("call %WORKSPACE%\\tests\\scripts\\run-corefx-tests.bat ${corefx_runtime_path} ${corefx_tests_dir} ${corefx_exclusion_file}")
 
-                    // Next, filter based on scenario.
+            } else { // !isCoreFxScenario(scenario)
 
-                    if (isJitStressScenario(scenario)) {
-                        if (configuration != 'Checked') {
-                            return
-                        }
+                // Unzip tests.
+                batchFile("powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::ExtractToDirectory('bin\\tests\\tests.zip', 'bin\\tests\\${osGroup}.${architecture}.${configuration}')")
 
-                        // CoreFx JIT stress tests currently only implemented for ARM.
-                        if (isCoreFxScenario(scenario) && (architecture != 'arm')) {
-                            return
-                        }
+                def buildCommands = ""
+
+                def coreRootLocation = "%WORKSPACE%\\bin\\tests\\Windows_NT.${architecture}.${configuration}\\Tests\\Core_Root"
+                def addEnvVariable =  { variable, value -> buildCommands += "set ${variable}=${value}\r\n"}
+                def addCommand = { cmd -> buildCommands += "${cmd}\r\n"}
+
+                // Make sure Command Extensions are enabled. Used so %ERRORLEVEL% is available.
+                addCommand("SETLOCAL ENABLEEXTENSIONS")
+
+                // For all jobs 
+                addEnvVariable("CORE_ROOT", coreRootLocation)
+                addEnvVariable("COMPlus_NoGuiOnAssert", "1")
+                addEnvVariable("COMPlus_ContinueOnAssert", "0")
+
+                // ARM legacy backend; this is an altjit.
+                if (architecture == 'armlb') {
+                    addEnvVariable("COMPlus_AltJit", "*")
+                    addEnvVariable("COMPlus_AltJitNgen", "*")
+                    addEnvVariable("COMPlus_AltJitName", "legacyjit.dll")
+                    addEnvVariable("COMPlus_AltJitAssertOnNYI", "1")
+                }
+
+                // If we are running a stress mode, we'll set those variables as well
+                if (isJitStressScenario(scenario) || isR2RStressScenario(scenario)) {
+                    def stressValues = null
+                    if (isJitStressScenario(scenario)) {
+                        stressValues = Constants.jitStressModeScenarios[scenario]
                     }
-                    else if (isR2RBaselineScenario(scenario)) {
-                        if (configuration != 'Checked' && configuration != 'Release') {
-                            return
-                        }
+                    else {
+                        stressValues = Constants.r2rStressScenarios[scenario]
                     }
-                    else if (isR2RStressScenario(scenario)) {
-                        if (configuration != 'Checked') {
-                            return
-                        }
+
+                    stressValues.each { key, value -> 
+                        addEnvVariable(key, value)
                     }
-                    else {
-                        // Skip scenarios
-                        switch (scenario) {
-                            case 'ilrt':
-                            case 'longgc':
-                            case 'gcsimulator':
-                                // Long GC tests take a long time on non-Release builds
-                                // ilrt is also Release only
-                                if (configuration != 'Release') {
-                                    return
-                                }
-                                break
+                }
 
-                            case 'jitdiff':
-                                if (configuration != 'Checked') {
-                                    return;
-                                }
-                                break
+                if (isR2RScenario(scenario)) {
+                    // Crossgen the framework assemblies.
+                    buildCommands += """
+@for %%F in (%CORE_ROOT%\\*.dll) do @call :PrecompileAssembly "%CORE_ROOT%" "%%F" %%~nxF
+@goto skip_PrecompileAssembly
 
-                            case 'gc_reliability_framework':
-                            case 'standalone_gc':
-                                if (configuration != 'Release' && configuration != 'Checked') {
-                                    return
-                                }
-                                break
+:PrecompileAssembly
+@REM Skip mscorlib since it is already precompiled.
+@if /I "%3" == "mscorlib.dll" exit /b 0
+@if /I "%3" == "mscorlib.ni.dll" exit /b 0
 
-                            case 'formatting':
-                                return
-                            case 'illink':
-                                if (os != 'Windows_NT' && os != 'Ubuntu') {
-                                    return
-                                }
-                                break
+"%CORE_ROOT%\\crossgen.exe" /Platform_Assemblies_Paths "%CORE_ROOT%" %2 >nul 2>nul
+@if "%errorlevel%" == "-2146230517" (
+    echo %2 is not a managed assembly.
+) else if "%errorlevel%" == "-2146234344" (
+    echo %2 is not a managed assembly.
+) else if %errorlevel% neq 0 (
+    echo Unable to precompile %2
+) else (
+    echo Precompiled %2
+)
+@exit /b 0
 
-                            case 'normal':
-                                // Nothing skipped
-                                break
+:skip_PrecompileAssembly
+"""
 
-                            case 'innerloop':
-                                // Nothing skipped
-                                if (!isValidPrTriggeredInnerLoopJob(os, architecture, configuration, false)) {
-                                    return
-                                }
-                                break
+                    // Set RunCrossGen variable to cause test wrappers to invoke their logic to run
+                    // crossgen on tests before running them.
+                    addEnvVariable("RunCrossGen", "true")
+                } // isR2RScenario(scenario)
 
-                            default:
-                                println("Unknown scenario: ${scenario}")
-                                assert false
-                                break
-                        }
-                    }
+                // Create the smarty command
+                def smartyCommand = "C:\\Tools\\Smarty.exe /noecid /noie /workers 9 /inc EXPECTED_PASS "
+                def addSmartyFlag = { flag -> smartyCommand += flag + " "}
+                def addExclude = { exclude -> addSmartyFlag("/exc " + exclude)}
+                def addArchSpecificExclude = { architectureToExclude, exclude -> if (architectureToExclude == "armlb") { addExclude("LEGACYJIT_" + exclude) } else { addExclude(exclude) } }
 
-                    // Done filtering. Now, create the jobs.
+                if (architecture == 'armlb') {
+                    addExclude("LEGACYJIT_FAIL")
+                }
 
-                    // =============================================================================================
-                    // Create the test job
-                    // =============================================================================================
+                // Exclude tests based on scenario.
+                Constants.validArmWindowsScenarios[scenario].each { excludeTag ->
+                    addArchSpecificExclude(architecture, excludeTag)
+                }
 
-                    def windowsArmJob = (os == "Windows_NT" && architecture in validWindowsNTCrossArches)
+                // Innerloop jobs run Pri-0 tests; everyone else runs Pri-1.
+                if (scenario == 'innerloop') {
+                    addExclude("pri1")
+                }
 
-                    def lowerConfiguration = configuration.toLowerCase()
-                    def osGroup = getOSGroup(os)
-                    def jobName = getJobName(configuration, architecture, os, scenario, false) + "_tst"
+                // Exclude any test marked LONG_RUNNING; these often exceed the standard timeout and fail as a result.
+                // TODO: We should create a "long running" job that runs these with a longer timeout.
+                addExclude("LONG_RUNNING")
 
-                    def inputCoreCLRBuildScenario = scenario == 'innerloop' ? 'innerloop' : 'normal'
-                    def inputCoreCLRBuildIsBuildOnly = false
-                    if (isCoreFxScenario(scenario)) {
-                        // Every CoreFx test depends on its own unique build.
-                        inputCoreCLRBuildScenario = scenario
-                        inputCoreCLRBuildIsBuildOnly = true
-                    }
-                    def inputCoreCLRFolderName = getJobFolder(inputCoreCLRBuildScenario)
-                    def inputCoreCLRBuildName = projectFolder + '/' +
-                        Utilities.getFullJobName(project, getJobName(configuration, architecture, os, inputCoreCLRBuildScenario, inputCoreCLRBuildIsBuildOnly), isPR, inputCoreCLRFolderName)
+                smartyCommand += "/lstFile Tests.lst"
 
-                    def inputWindowsTestsBuildName = ""
-                    if (windowsArmJob != true) {
-                        // If this is a stress scenario, there isn't any difference in the build job, so we didn't create a build only
-                        // job for Windows_NT specific to that stress mode. Just copy from the default scenario.
+                def testListArch = [
+                    'arm64': 'arm64',
+                    'arm': 'arm',
+                    'armlb': 'arm'
+                ]
 
-                        def testBuildScenario = scenario == 'innerloop' ? 'innerloop' : 'normal'
+                def archLocation = testListArch[architecture]
 
-                        def inputWindowsTestBuildArch = architecture
-                        if (architecture == "arm64" && os != "Windows_NT") {
-                            // Use the x64 test build for arm64 unix
-                            inputWindowsTestBuildArch = "x64"
-                        }
+                addCommand("copy %WORKSPACE%\\tests\\${archLocation}\\Tests.lst bin\\tests\\${osGroup}.${architecture}.${configuration}")
+                addCommand("pushd bin\\tests\\${osGroup}.${architecture}.${configuration}")
+                addCommand("${smartyCommand}")
 
-                        if (isJitStressScenario(scenario)) {
-                            inputWindowsTestsBuildName = projectFolder + '/' +
-                                Utilities.getFullJobName(project, getJobName(configuration, inputWindowsTestBuildArch, 'windows_nt', testBuildScenario, false), isPR)
-                        } else {
-                            inputWindowsTestsBuildName = projectFolder + '/' +
-                                Utilities.getFullJobName(project, getJobName(configuration, inputWindowsTestBuildArch, 'windows_nt', testBuildScenario, true), isPR)
-                        }
-                    } // if (windowsArmJob != true)
+                // Save the errorlevel from the smarty command to be used as the errorlevel of this batch file.
+                // However, we also need to remove all the variables that were set during this batch file, so we
+                // can run the ZIP powershell command (below) in a clean environment. (We can't run the powershell
+                // command with the COMPlus_AltJit variables set, for example.) To do that, we do ENDLOCAL as well
+                // as save the current errorlevel on the same line. This works because CMD evaluates the %errorlevel%
+                // variable expansion (or any variable expansion on the line) BEFORE it executes the ENDLOCAL command.
+                // Note that the ENDLOCAL also undoes the pushd command, but we add the popd here for clarity.
+                addCommand("popd & ENDLOCAL & set __save_smarty_errorlevel=%errorlevel%")
 
-                    def serverGCString = ''
-                    def testOpts = ''
+                // ZIP up the smarty output, no matter what the smarty result.
+                addCommand("powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::CreateFromDirectory('.\\bin\\tests\\${osGroup}.${architecture}.${configuration}\\Smarty.run.0', '.\\bin\\tests\\${osGroup}.${architecture}.${configuration}\\Smarty.run.0.zip')\"")
 
-                    if (windowsArmJob != true) {
-                        // Enable Server GC for Ubuntu PR builds
-                        if (os == 'Ubuntu' && isPR) {
-                            serverGCString = '--useServerGC'
-                        }
+                addCommand("echo %errorlevel%")
+                addCommand("dir .\\bin\\tests\\${osGroup}.${architecture}.${configuration}")
 
-                        if (isR2RScenario(scenario)) {
+                // Use the smarty errorlevel as the script errorlevel.
+                addCommand("exit /b %__save_smarty_errorlevel%")
 
-                            testOpts += ' --crossgen --runcrossgentests'
+                batchFile(buildCommands)
+            } // non-corefx testing
+        } // steps
+    } // job
 
-                            if (scenario == 'r2r_jitstress1') {
-                                testOpts += ' --jitstress=1'
-                            }
-                            else if (scenario == 'r2r_jitstress2') {
-                                testOpts += ' --jitstress=2'
-                            }
-                            else if (scenario == 'r2r_jitstressregs1') {
-                                testOpts += ' --jitstressregs=1'
-                            }
-                            else if (scenario == 'r2r_jitstressregs2') {
-                                testOpts += ' --jitstressregs=2'
-                            }
-                            else if (scenario == 'r2r_jitstressregs3') {
-                                testOpts += ' --jitstressregs=3'
-                            }
-                            else if (scenario == 'r2r_jitstressregs4') {
-                                testOpts += ' --jitstressregs=4'
-                            }
-                            else if (scenario == 'r2r_jitstressregs8') {
-                                testOpts += ' --jitstressregs=8'
-                            }
-                            else if (scenario == 'r2r_jitstressregs0x10') {
-                                testOpts += ' --jitstressregs=0x10'
-                            }
-                            else if (scenario == 'r2r_jitstressregs0x80') {
-                                testOpts += ' --jitstressregs=0x80'
-                            }
-                            else if (scenario == 'r2r_jitstressregs0x1000') {
-                                testOpts += ' --jitstressregs=0x1000'
-                            }
-                            else if (scenario == 'r2r_jitminopts') {
-                                testOpts += ' --jitminopts'
-                            }
-                            else if (scenario == 'r2r_jitforcerelocs') {
-                                testOpts += ' --jitforcerelocs'
-                            }
-                            else if (scenario == 'r2r_gcstress15') {
-                                testOpts += ' --gcstresslevel=0xF'
-                            }
-                        }
-                        else if (scenario == 'jitdiff') {
-                            testOpts += ' --jitdisasm --crossgen'
-                        }
-                        else if (scenario == 'illink') {
-                            testOpts += ' --link=\$WORKSPACE/linker/linker/bin/netcore_Release/netcoreapp2.0/ubuntu-x64/publish/illink'
-                        }
-                        else if (isLongGc(scenario)) {
-                            // Long GC tests behave very poorly when they are not
-                            // the only test running (many of them allocate until OOM).
-                            testOpts += ' --sequential'
-
-                            // A note - runtest.sh does have "--long-gc" and "--gcsimulator" options
-                            // for running long GC and GCSimulator tests, respectively. We don't use them
-                            // here because using a playlist file produces much more readable output on the CI machines
-                            // and reduces running time.
-                            //
-                            // The Long GC playlist contains all of the tests that are
-                            // going to be run. The GCSimulator playlist contains all of
-                            // the GC simulator tests.
-                            if (scenario == 'longgc') {
-                                testOpts += ' --long-gc --playlist=./tests/longRunningGcTests.txt'
-                            }
-                            else if (scenario == 'gcsimulator') {
-                                testOpts += ' --gcsimulator --playlist=./tests/gcSimulatorTests.txt'
-                            }
-                        }
-                        else if (isGcReliabilityFramework(scenario)) {
-                            testOpts += ' --build-overlay-only'
-                        }
-                        else if (scenario == 'standalone_gc') {
-                            if (osGroup == 'OSX') {
-                                testOpts += ' --gcname=libclrgc.dylib'
-                            }
-                            else if (osGroup == 'Linux') {
-                                testOpts += ' --gcname=libclrgc.so'
-                            }
-                            else {
-                                println("Unexpected OS group: ${osGroup} for os ${os}")
-                                assert false
-                            }
-                        }
-                    } // if (windowsArmJob != true)
+    if (!isCoreFxScenario(scenario)) {
+        Utilities.addArchival(newJob, "bin/tests/${osGroup}.${architecture}.${configuration}/Smarty.run.0/*.smrt", '', true, false)
 
-                    def folder = getJobFolder(scenario)
-                    def newJob = job(Utilities.getFullJobName(project, jobName, isPR, folder)) {
-                        // Add parameters for the inputs
+        // Archive a ZIP file of the entire Smarty.run.0 directory. This is possibly a little too much,
+        // but there is no easy way to only archive the HTML/TXT files of the failing tests, so we get
+        // all the passing test info as well. Not necessarily a bad thing, but possibly somewhat large.
+        Utilities.addArchival(newJob, "bin/tests/${osGroup}.${architecture}.${configuration}/Smarty.run.0.zip", '', true, false)
+    }
 
-                        if (windowsArmJob == true) {
-                            parameters {
-                                stringParam('CORECLR_BUILD', '', "Build number to copy CoreCLR ${osGroup} binaries from")
-                            }
-                        }
-                        else {
-                            parameters {
-                                stringParam('CORECLR_WINDOWS_BUILD', '', 'Build number to copy CoreCLR Windows test binaries from')
-                                stringParam('CORECLR_BUILD', '', "Build number to copy CoreCLR ${osGroup} binaries from")
-                            }
-                        }
+    return newJob
+}
 
-                        steps {
-                            // Set up the copies
+// Create a test job not covered by the "Windows ARM" case that will be used by a flow job.
+// E.g., non-Windows tests.
+// Returns the newly created job.
+def static CreateOtherTestJob(def dslFactory, def project, def branch, def architecture, def os, def configuration, def scenario, def isPR, def inputCoreCLRBuildName, def inputTestsBuildName)
+{
+    def isUbuntuArmJob = ((os == "Ubuntu") && (architecture == 'arm')) // ARM Ubuntu running on hardware (not emulator)
 
-                            // Coreclr build containing the tests and mscorlib
-                            // pri1 jobs still need to copy windows_nt built tests
-                            if (windowsArmJob != true) {
-                                copyArtifacts(inputWindowsTestsBuildName) {
-                                    excludePatterns('**/testResults.xml', '**/*.ni.dll')
-                                    buildSelector {
-                                        buildNumber('${CORECLR_WINDOWS_BUILD}')
-                                    }
-                                }
-                            }
+    def osGroup = getOSGroup(os)
+    def jobName = getJobName(configuration, architecture, os, scenario, false) + "_tst"
 
-                            // Coreclr build we are trying to test
-                            //
-                            //  ** NOTE ** This will, correctly, overwrite the CORE_ROOT from the Windows test archive
+    def testOpts = ''
+    def useServerGC = false
 
-                            copyArtifacts(inputCoreCLRBuildName) {
-                                excludePatterns('**/testResults.xml', '**/*.ni.dll')
-                                buildSelector {
-                                    buildNumber('${CORECLR_BUILD}')
-                                }
-                            }
+    // Enable Server GC for Ubuntu PR builds
+    // REVIEW: why? Does this apply to all architectures? Why only PR?
+    if (os == 'Ubuntu' && isPR) {
+        testOpts += ' --useServerGC'
+        useServerGC = true
+    }
 
-                            // Windows CoreCLR Arm(64) will restore corefx
-                            // packages correctly.
-                            //
-                            // In addition, test steps are entirely different
-                            // because we do not have a unified runner
-                            if (windowsArmJob != true) {
-                                def corefxFolder = Utilities.getFolderName('dotnet/corefx') + '/' + Utilities.getFolderName(branch)
-
-                                // HACK -- Arm64 does not have corefx jobs yet.
-                                // Clone corefx and build the native packages overwriting the x64 packages.
-                                if (architecture == 'arm64') {
-                                    shell("mkdir -p ./bin/CoreFxBinDir")
-                                    shell("cp ./bin/Product/Linux.arm64.${configuration}/corefxNative/* ./bin/CoreFxBinDir")
-                                    shell("chmod +x ./bin/Product/Linux.arm64.${configuration}/corerun")
-                                }
-                                else if (architecture == 'x86') {
-                                    shell("mkdir ./bin/CoreFxNative")
-
-                                    copyArtifacts("${corefxFolder}/ubuntu16.04_x86_release") {
-                                        includePatterns('bin/build.tar.gz')
-                                        targetDirectory('bin/CoreFxNative')
-                                        buildSelector {
-                                            latestSuccessful(true)
-                                        }
-                                    }
+    if (isR2RScenario(scenario)) {
 
-                                    shell("tar -xf ./bin/CoreFxNative/bin/build.tar.gz -C ./bin/CoreFxBinDir")
-                                }
+        testOpts += ' --crossgen --runcrossgentests'
 
-                                // Unzip the tests first.  Exit with 0
-                                shell("unzip -q -o ./bin/tests/tests.zip -d ./bin/tests/${osGroup}.${architecture}.${configuration} || exit 0")
-                                shell("rm -r ./bin/tests/${osGroup}.${architecture}.${configuration}/Tests/Core_Root || exit 0")
+        if (scenario == 'r2r_jitstress1') {
+            testOpts += ' --jitstress=1'
+        }
+        else if (scenario == 'r2r_jitstress2') {
+            testOpts += ' --jitstress=2'
+        }
+        else if (scenario == 'r2r_jitstressregs1') {
+            testOpts += ' --jitstressregs=1'
+        }
+        else if (scenario == 'r2r_jitstressregs2') {
+            testOpts += ' --jitstressregs=2'
+        }
+        else if (scenario == 'r2r_jitstressregs3') {
+            testOpts += ' --jitstressregs=3'
+        }
+        else if (scenario == 'r2r_jitstressregs4') {
+            testOpts += ' --jitstressregs=4'
+        }
+        else if (scenario == 'r2r_jitstressregs8') {
+            testOpts += ' --jitstressregs=8'
+        }
+        else if (scenario == 'r2r_jitstressregs0x10') {
+            testOpts += ' --jitstressregs=0x10'
+        }
+        else if (scenario == 'r2r_jitstressregs0x80') {
+            testOpts += ' --jitstressregs=0x80'
+        }
+        else if (scenario == 'r2r_jitstressregs0x1000') {
+            testOpts += ' --jitstressregs=0x1000'
+        }
+        else if (scenario == 'r2r_jitminopts') {
+            testOpts += ' --jitminopts'
+        }
+        else if (scenario == 'r2r_jitforcerelocs') {
+            testOpts += ' --jitforcerelocs'
+        }
+        else if (scenario == 'r2r_gcstress15') {
+            testOpts += ' --gcstresslevel=0xF'
+        }
+    }
+    else if (scenario == 'jitdiff') {
+        testOpts += ' --jitdisasm --crossgen'
+    }
+    else if (scenario == 'illink') {
+        testOpts += ' --link=\$WORKSPACE/linker/linker/bin/netcore_Release/netcoreapp2.0/ubuntu-x64/publish/illink'
+    }
+    else if (isLongGc(scenario)) {
+        // Long GC tests behave very poorly when they are not
+        // the only test running (many of them allocate until OOM).
+        testOpts += ' --sequential'
+
+        // A note - runtest.sh does have "--long-gc" and "--gcsimulator" options
+        // for running long GC and GCSimulator tests, respectively. We don't use them
+        // here because using a playlist file produces much more readable output on the CI machines
+        // and reduces running time.
+        //
+        // The Long GC playlist contains all of the tests that are
+        // going to be run. The GCSimulator playlist contains all of
+        // the GC simulator tests.
+        if (scenario == 'longgc') {
+            testOpts += ' --long-gc --playlist=./tests/longRunningGcTests.txt'
+        }
+        else if (scenario == 'gcsimulator') {
+            testOpts += ' --gcsimulator --playlist=./tests/gcSimulatorTests.txt'
+        }
+    }
+    else if (isGcReliabilityFramework(scenario)) {
+        testOpts += ' --build-overlay-only'
+    }
+    else if (scenario == 'standalone_gc') {
+        if (osGroup == 'OSX') {
+            testOpts += ' --gcname=libclrgc.dylib'
+        }
+        else if (osGroup == 'Linux') {
+            testOpts += ' --gcname=libclrgc.so'
+        }
+        else {
+            println("Unexpected OS group: ${osGroup} for os ${os}")
+            assert false
+        }
+    }
 
-                                shell("./build-test.sh ${architecture} ${configuration} generatelayoutonly")
+    def jobFolder = getJobFolder(scenario)
+    def newJob = dslFactory.job(Utilities.getFullJobName(project, jobName, isPR, jobFolder)) {
+        parameters {
+            stringParam('CORECLR_WINDOWS_BUILD', '', 'Build number to copy CoreCLR Windows test binaries from')
+            stringParam('CORECLR_BUILD', '', "Build number to copy CoreCLR ${osGroup} binaries from")
+        }
 
-                                // Execute the tests
-                                def runDocker = isNeedDocker(architecture, os, false)
-                                def dockerPrefix = ""
-                                def dockerCmd = ""
-                                if (runDocker) {
-                                    def dockerImage = getDockerImageName(architecture, os, false)
-                                    dockerPrefix = "docker run -i --rm -v \${WORKSPACE}:\${WORKSPACE} -w \${WORKSPACE} "
-                                    dockerCmd = dockerPrefix + "${dockerImage} "
-                                }
+        steps {
+            // Set up the copies
 
-                                // If we are running a stress mode, we'll set those variables first
-                                def testEnvOpt = ""
-                                if (isJitStressScenario(scenario)) {
-                                    def scriptFileName = "\$WORKSPACE/set_stress_test_env.sh"
-                                    def envScriptCmds = envScriptCreate(os, scriptFileName)
-                                    envScriptCmds += envScriptSetStressModeVariables(os, Constants.jitStressModeScenarios[scenario], scriptFileName)
-                                    envScriptCmds += envScriptFinalize(os, scriptFileName)
-                                    shell("${envScriptCmds}")
-                                    testEnvOpt = "--test-env=" + scriptFileName
-                                }
+            // Coreclr build containing the tests and mscorlib
+            // pri1 jobs still need to copy windows_nt built tests
+            assert inputTestsBuildName != null
+            copyArtifacts(inputTestsBuildName) {
+                excludePatterns('**/testResults.xml', '**/*.ni.dll')
+                buildSelector {
+                    buildNumber('${CORECLR_WINDOWS_BUILD}')
+                }
+            }
 
-                                if (isGCStressRelatedTesting(scenario)) {
-                                    shell('./init-tools.sh')
-                                }
+            // Coreclr build we are trying to test
+            //
+            //  ** NOTE ** This will, correctly, overwrite the CORE_ROOT from the Windows test archive
 
-                                shell("""${dockerCmd}./tests/runtest.sh \\
-                --testRootDir=\"\${WORKSPACE}/bin/tests/${osGroup}.${architecture}.${configuration}\" \\
-                --coreOverlayDir=\"\${WORKSPACE}/bin/tests/${osGroup}.${architecture}.${configuration}/Tests/Core_Root\" \\
-                --testNativeBinDir=\"\${WORKSPACE}/bin/obj/${osGroup}.${architecture}.${configuration}/tests\" \\
-                --copyNativeTestBin --limitedDumpGeneration ${testEnvOpt} ${serverGCString} ${testOpts}""")
-
-                                if (isGcReliabilityFramework(scenario)) {
-                                    // runtest.sh doesn't actually execute the reliability framework - do it here.
-                                    if (serverGCString != '') {
-                                        if (runDocker) {
-                                            dockerCmd = dockerPrefix + "-e COMPlus_gcServer=1 ${dockerImage} "
-                                        }
-                                        else {
-                                            shell("export COMPlus_gcServer=1")
-                                        }
-                                    }
+            copyArtifacts(inputCoreCLRBuildName) {
+                excludePatterns('**/testResults.xml', '**/*.ni.dll')
+                buildSelector {
+                    buildNumber('${CORECLR_BUILD}')
+                }
+            }
 
-                                    shell("${dockerCmd}./tests/scripts/run-gc-reliability-framework.sh ${architecture} ${configuration}")
-                                }
-                            } 
-                            else { // windowsArmJob == true
-                                
-                                if (isCoreFxScenario(scenario)) {
+            if (isUbuntuArmJob) {
+                // Add some useful information to the log file. Ignore return codes.
+                shell("uname -a || true")
+            }
 
-                                    // Only arm supported for corefx testing now.
-                                    assert architecture == 'arm'
+            if (architecture == 'arm64') {
+                shell("mkdir -p ./bin/CoreFxBinDir")
+                shell("cp ./bin/Product/Linux.arm64.${configuration}/corefxNative/* ./bin/CoreFxBinDir")
+                shell("chmod +x ./bin/Product/Linux.arm64.${configuration}/corerun")
+            }
+            else if (architecture == 'x86') {
+                shell("mkdir ./bin/CoreFxNative")
 
-                                    // Unzip CoreFx runtime
-                                    batchFile("powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::ExtractToDirectory('_\\fx\\fxruntime.zip', '_\\fx\\bin\\testhost\\netcoreapp-Windows_NT-Release-arm')")
+                def corefxFolder = Utilities.getFolderName('dotnet/corefx') + '/' + Utilities.getFolderName(branch)
 
-                                    // Unzip CoreFx tests.
-                                    batchFile("powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::ExtractToDirectory('_\\fx\\fxtests.zip', '_\\fx\\bin\\tests')")
+                copyArtifacts("${corefxFolder}/ubuntu16.04_x86_release") {
+                    includePatterns('bin/build.tar.gz')
+                    targetDirectory('bin/CoreFxNative')
+                    buildSelector {
+                        latestSuccessful(true)
+                    }
+                }
 
-                                    // Add the script to run the corefx tests
-                                    def corefx_runtime_path   = "%WORKSPACE%\\_\\fx\\bin\\testhost\\netcoreapp-Windows_NT-Release-arm"
-                                    def corefx_tests_dir      = "%WORKSPACE%\\_\\fx\\bin\\tests"
-                                    def corefx_exclusion_file = "%WORKSPACE%\\tests\\arm\\corefx_test_exclusions.txt"
-                                    batchFile("call %WORKSPACE%\\tests\\scripts\\run-corefx-tests.bat ${corefx_runtime_path} ${corefx_tests_dir} ${corefx_exclusion_file}")
+                shell("tar -xf ./bin/CoreFxNative/bin/build.tar.gz -C ./bin/CoreFxBinDir")
+            }
 
-                                } else { // !isCoreFxScenario(scenario)
+            // Unzip the tests first.  Exit with 0
+            shell("unzip -q -o ./bin/tests/tests.zip -d ./bin/tests/${osGroup}.${architecture}.${configuration} || exit 0")
+            shell("rm -r ./bin/tests/${osGroup}.${architecture}.${configuration}/Tests/Core_Root || exit 0")
+
+            // For arm Ubuntu (on hardware), we do the "build-test" step on the build machine, not on the test
+            // machine. The arm Ubuntu test machines do no building -- they have no CLI, for example.
+            // We should probably do the "generatelayoutonly" step on the build machine for all architectures.
+            // However, it's believed that perhaps there's an issue with executable permission bits not getting
+            // copied correctly.
+            if (isUbuntuArmJob) {
+                def lowerConfiguration = configuration.toLowerCase()
+                shell("unzip -o ./coreroot.${lowerConfiguration}.zip || exit 0")      // unzips to ./bin/tests/Linux.arm.${configuration}/Tests/Core_Root
+                shell("unzip -o ./testnativebin.${lowerConfiguration}.zip || exit 0") // unzips to ./bin/obj/Linux.arm.${configuration}/tests
+            }
+            else {
+                shell("./build-test.sh ${architecture} ${configuration} generatelayoutonly")
+            }
 
-                                    // Unzip tests.
-                                    batchFile("powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::ExtractToDirectory('bin\\tests\\tests.zip', 'bin\\tests\\${osGroup}.${architecture}.${configuration}')")
+            // Execute the tests
+            def runDocker = isNeedDocker(architecture, os, false)
+            def dockerPrefix = ""
+            def dockerCmd = ""
+            if (runDocker) {
+                def dockerImage = getDockerImageName(architecture, os, false)
+                dockerPrefix = "docker run -i --rm -v \${WORKSPACE}:\${WORKSPACE} -w \${WORKSPACE} "
+                dockerCmd = dockerPrefix + "${dockerImage} "
+            }
 
-                                    def buildCommands = ""
+            // If we are running a stress mode, we'll set those variables first
+            if (isJitStressScenario(scenario)) {
+                def scriptFileName = "\${WORKSPACE}/set_stress_test_env.sh"
+                def envScriptCmds = envScriptCreate(os, scriptFileName)
+                envScriptCmds += envScriptSetStressModeVariables(os, Constants.jitStressModeScenarios[scenario], scriptFileName)
+                envScriptCmds += envScriptFinalize(os, scriptFileName)
+                shell("${envScriptCmds}")
+                testOpts += " --test-env=${scriptFileName}"
+            }
 
-                                    def coreRootLocation = "%WORKSPACE%\\bin\\tests\\Windows_NT.${architecture}.${configuration}\\Tests\\Core_Root"
-                                    def addEnvVariable =  { variable, value -> buildCommands += "set ${variable}=${value}\r\n"}
-                                    def addCommand = { cmd -> buildCommands += "${cmd}\r\n"}
+            // TODO: how to handle GCStress-related testing for Ubuntu/arm?
+            if (isGCStressRelatedTesting(scenario)) {
+                shell('./init-tools.sh')
+            }
 
-                                    // Make sure Command Extensions are enabled. Used so %ERRORLEVEL% is available.
-                                    addCommand("SETLOCAL ENABLEEXTENSIONS")
-        
-                                    // For all jobs 
-                                    addEnvVariable("CORE_ROOT", coreRootLocation)
-
-                                    addEnvVariable("COMPlus_NoGuiOnAssert", "1")
-                                    addEnvVariable("COMPlus_ContinueOnAssert", "0")
-
-                                    // ARM legacy backend; this is an altjit.
-                                    if (architecture == "armlb") {
-                                        addEnvVariable("COMPlus_AltJit", "*")
-                                        addEnvVariable("COMPlus_AltJitNgen", "*")
-                                        addEnvVariable("COMPlus_AltJitName", "legacyjit.dll")
-                                        addEnvVariable("COMPlus_AltJitAssertOnNYI", "1")
-                                    }
+            def runScript = ""
+            if (isUbuntuArmJob) {
+                // Use 'runtesttilstable.sh' to rerun failing tests (in sequential mode);
+                // there are many tests that pass on rerun (currently), and we don't want
+                // that flakiness to affect overall test job robustness.
+                runScript = "${dockerCmd}./tests/runtesttilstable.sh"
+            } else {
+                runScript = "${dockerCmd}./tests/runtest.sh"
+            }
 
-                                    // If we are running a stress mode, we'll set those variables as well
-                                    if (isJitStressScenario(scenario) || isR2RStressScenario(scenario)) {
-                                        def stressValues = null
-                                        if (isJitStressScenario(scenario)) {
-                                            stressValues = Constants.jitStressModeScenarios[scenario]
-                                        }
-                                        else {
-                                            stressValues = Constants.r2rStressScenarios[scenario]
-                                        }
-
-                                        stressValues.each { key, value -> 
-                                            addEnvVariable(key, value)
-                                        }
-                                    }
+            shell("""\
+${runScript} \\
+    --testRootDir=\"\${WORKSPACE}/bin/tests/${osGroup}.${architecture}.${configuration}\" \\
+    --coreOverlayDir=\"\${WORKSPACE}/bin/tests/${osGroup}.${architecture}.${configuration}/Tests/Core_Root\" \\
+    --testNativeBinDir=\"\${WORKSPACE}/bin/obj/${osGroup}.${architecture}.${configuration}/tests\" \\
+    --copyNativeTestBin --limitedDumpGeneration ${testOpts}""")
 
-                                    if (isR2RScenario(scenario)) {
-                                        // Crossgen the framework assemblies.
-                                        buildCommands += """
-@for %%F in (%CORE_ROOT%\\*.dll) do @call :PrecompileAssembly "%CORE_ROOT%" "%%F" %%~nxF
-@goto skip_PrecompileAssembly
+            if (isGcReliabilityFramework(scenario)) {
+                // runtest.sh doesn't actually execute the reliability framework - do it here.
+                if (useServerGC) {
+                    if (runDocker) {
+                        dockerCmd = dockerPrefix + "-e COMPlus_gcServer=1 ${dockerImage} "
+                    }
+                    else {
+                        shell("export COMPlus_gcServer=1")
+                    }
+                }
 
-:PrecompileAssembly
-@REM Skip mscorlib since it is already precompiled.
-@if /I "%3" == "mscorlib.dll" exit /b 0
-@if /I "%3" == "mscorlib.ni.dll" exit /b 0
+                shell("${dockerCmd}./tests/scripts/run-gc-reliability-framework.sh ${architecture} ${configuration}")
+            }
+        } // steps
+    } // job
+
+    // Experimental: If on Ubuntu 14.04, then attempt to pull in crash dump links
+    if (os in ['Ubuntu']) {
+        SummaryBuilder summaries = new SummaryBuilder()
+        summaries.addLinksSummaryFromFile('Crash dumps from this run:', 'dumplings.txt')
+        summaries.emit(newJob)
+    }
 
-"%CORE_ROOT%\\crossgen.exe" /Platform_Assemblies_Paths "%CORE_ROOT%" %2 >nul 2>nul
-@if "%errorlevel%" == "-2146230517" (
-    echo %2 is not a managed assembly.
-) else if "%errorlevel%" == "-2146234344" (
-    echo %2 is not a managed assembly.
-) else if %errorlevel% neq 0 (
-    echo Unable to precompile %2
-) else (
-    echo Precompiled %2
-)
-@exit /b 0
+    Utilities.addArchival(newJob, "bin/tests/${osGroup}.${architecture}.${configuration}/coreclrtests.*.txt")
+    Utilities.addXUnitDotNETResults(newJob, '**/coreclrtests.xml')
 
-:skip_PrecompileAssembly
-"""
+    return newJob
+}
 
-                                        // Set RunCrossGen variable to cause test wrappers to invoke their logic to run
-                                        // crossgen on tests before running them.
-                                        addEnvVariable("RunCrossGen", "true")
-                                    } // isR2RScenario(scenario)
+// Create a test job that will be used by a flow job.
+// Returns the newly created job.
+def static CreateTestJob(def dslFactory, def project, def branch, def architecture, def os, def configuration, def scenario, def isPR, def inputCoreCLRBuildName, def inputTestsBuildName)
+{
+    def windowsArmJob = ((os == "Windows_NT") && (architecture in Constants.armWindowsCrossArchitectureList))
 
-                                    // Create the smarty command
-                                    def smartyCommand = "C:\\Tools\\Smarty.exe /noecid /noie /workers 9 /inc EXPECTED_PASS "
-                                    def addSmartyFlag = { flag -> smartyCommand += flag + " "}
-                                    def addExclude = { exclude -> addSmartyFlag("/exc " + exclude)}
+    def newJob = null
+    if (windowsArmJob) {
+        assert inputTestsBuildName == null
+        newJob = CreateWindowsArmTestJob(dslFactory, project, architecture, os, configuration, scenario, isPR, inputCoreCLRBuildName)
+    } else {
+        newJob = CreateOtherTestJob(dslFactory, project, branch, architecture, os, configuration, scenario, isPR, inputCoreCLRBuildName, inputTestsBuildName)
+    }
 
-                                    def addArchSpecificExclude = { architectureToExclude, exclude -> if (architectureToExclude == "armlb") { addExclude("LEGACYJIT_" + exclude) } else { addExclude(exclude) } }
+    setJobMachineAffinity(architecture, os, false, true, false, newJob) // isBuildJob = false, isTestJob = true, isFlowJob = false
 
-                                    if (architecture == "armlb") {
-                                        addExclude("LEGACYJIT_FAIL")
-                                    }
+    addToViews(newJob, isPR, architecture, os)
 
-                                    // Exclude tests based on scenario.
-                                    Constants.validArmWindowsScenarios[scenario].each { excludeTag ->
-                                        addArchSpecificExclude(architecture, excludeTag)
-                                    }
+    if (scenario == 'jitdiff') {
+        def osGroup = getOSGroup(os)
+        Utilities.addArchival(newJob, "bin/tests/${osGroup}.${architecture}.${configuration}/dasm/**")
+    }
 
-                                    // Innerloop jobs run Pri-0 tests; everyone else runs Pri-1.
-                                    if (scenario == 'innerloop') {
-                                        addExclude("pri1")
-                                    }
+    Utilities.standardJobSetup(newJob, project, isPR, "*/${branch}")
+    setJobTimeout(newJob, isPR, architecture, configuration, scenario, false)
 
-                                    // Exclude any test marked LONG_RUNNING; these often exceed the standard timeout and fail as a result.
-                                    // TODO: We should create a "long running" job that runs these with a longer timeout.
-                                    addExclude("LONG_RUNNING")
+    return newJob
+}
 
-                                    smartyCommand += "/lstFile Tests.lst"
+// Create a flow job to tie together a build job with the given test job.
+// Returns the new flow job.
+def static CreateFlowJob(def dslFactory, def project, def branch, def architecture, def os, def configuration, def scenario, def isPR, def fullTestJobName, def inputCoreCLRBuildName, def inputTestsBuildName)
+{
+    // Windows CoreCLR build and Linux CoreCLR build (in parallel) ->
+    // Linux CoreCLR test
+    def flowJobName = getJobName(configuration, architecture, os, scenario, false) + "_flow"
+    def jobFolder = getJobFolder(scenario)
 
-                                    def testListArch = [
-                                        'arm64': 'arm64',
-                                        'arm': 'arm',
-                                        'armlb': 'arm'
-                                    ]
+    def newFlowJob = null
 
-                                    def archLocation = testListArch[architecture]
+    def windowsArmJob = ((os == "Windows_NT") && (architecture in Constants.armWindowsCrossArchitectureList))
+    if (windowsArmJob) {
 
-                                    addCommand("copy %WORKSPACE%\\tests\\${archLocation}\\Tests.lst bin\\tests\\${osGroup}.${architecture}.${configuration}")
-                                    addCommand("pushd bin\\tests\\${osGroup}.${architecture}.${configuration}")
-                                    addCommand("${smartyCommand}")
+        assert inputTestsBuildName == null
 
-                                    // Save the errorlevel from the smarty command to be used as the errorlevel of this batch file.
-                                    // However, we also need to remove all the variables that were set during this batch file, so we
-                                    // can run the ZIP powershell command (below) in a clean environment. (We can't run the powershell
-                                    // command with the COMPlus_AltJit variables set, for example.) To do that, we do ENDLOCAL as well
-                                    // as save the current errorlevel on the same line. This works because CMD evaluates the %errorlevel%
-                                    // variable expansion (or any variable expansion on the line) BEFORE it executes the ENDLOCAL command.
-                                    // Note that the ENDLOCAL also undoes the pushd command, but we add the popd here for clarity.
-                                    addCommand("popd & ENDLOCAL & set __save_smarty_errorlevel=%errorlevel%")
+        // For Windows arm jobs there is no reason to build a parallel test job.
+        // The product build supports building and archiving the tests.
 
-                                    // ZIP up the smarty output, no matter what the smarty result.
-                                    addCommand("powershell -NoProfile -Command \"Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::CreateFromDirectory('.\\bin\\tests\\${osGroup}.${architecture}.${configuration}\\Smarty.run.0', '.\\bin\\tests\\${osGroup}.${architecture}.${configuration}\\Smarty.run.0.zip')\"")
+        newFlowJob = dslFactory.buildFlowJob(Utilities.getFullJobName(project, flowJobName, isPR, jobFolder)) {
+                        buildFlow("""\
+coreclrBuildJob = build(params, '${inputCoreCLRBuildName}')
 
-                                    addCommand("echo %errorlevel%")
-                                    addCommand("dir .\\bin\\tests\\${osGroup}.${architecture}.${configuration}")
+// And then build the test build
+build(params + [CORECLR_BUILD: coreclrBuildJob.build.number], '${fullTestJobName}')
+""")
+        }
+        JobReport.Report.addReference(inputCoreCLRBuildName)
+        JobReport.Report.addReference(fullTestJobName)
+    }
+    else {
+        newFlowJob = dslFactory.buildFlowJob(Utilities.getFullJobName(project, flowJobName, isPR, jobFolder)) {
+                        buildFlow("""\
+// Build the input jobs in parallel
+parallel (
+{ coreclrBuildJob = build(params, '${inputCoreCLRBuildName}') },
+{ windowsBuildJob = build(params, '${inputTestsBuildName}') }
+)
 
-                                    // Use the smarty errorlevel as the script errorlevel.
-                                    addCommand("exit /b %__save_smarty_errorlevel%")
+// And then build the test build
+build(params + [CORECLR_BUILD: coreclrBuildJob.build.number,
+                CORECLR_WINDOWS_BUILD: windowsBuildJob.build.number], '${fullTestJobName}')
+""")
+        }
+        JobReport.Report.addReference(inputCoreCLRBuildName)
+        JobReport.Report.addReference(inputTestsBuildName)
+        JobReport.Report.addReference(fullTestJobName)
+    }
 
-                                    batchFile(buildCommands)
-                                } // non-corefx testing
-                            } // windowsArmJob == true
-                        } // steps
-                    } // job
+    addToViews(newFlowJob, isPR, architecture, os)
 
-                    addToViews(newJob, isPR, architecture, os)
+    setJobMachineAffinity(architecture, os, false, false, true, newFlowJob) // isBuildJob = false, isTestJob = false, isFlowJob = true
 
-                    if (scenario == 'jitdiff') {
-                        Utilities.addArchival(newJob, "bin/tests/${osGroup}.${architecture}.${configuration}/dasm/**")
-                    }
+    Utilities.standardJobSetup(newFlowJob, project, isPR, "*/${branch}")
+    addTriggers(newFlowJob, branch, isPR, architecture, os, configuration, scenario, true, false) // isFlowJob==true, isWindowsBuildOnlyJob==false
 
-                    // Experimental: If on Ubuntu 14.04, then attempt to pull in crash dump links
-                    if (os in ['Ubuntu']) {
-                        SummaryBuilder summaries = new SummaryBuilder()
-                        summaries.addLinksSummaryFromFile('Crash dumps from this run:', 'dumplings.txt')
-                        summaries.emit(newJob)
-                    }
+    return newFlowJob
+}
 
-                    def affinityOptions = null
+// Determine if we should generate a flow job for the given parameters.
+// Returns true if the job should be generated.
+def static shouldGenerateFlowJob(def scenario, def isPR, def architecture, def configuration, def os)
+{
+    // The "innerloop" (Pri-0 testing) scenario is only available as PR triggered.
+    // All other scenarios do Pri-1 testing.
+    if (scenario == 'innerloop' && !isPR) {
+        return false
+    }
 
-                    if (windowsArmJob == true) {
-                        affinityOptions = [
-                            "use_arm64_build_machine" : false
-                        ]
-                    }
+    // Filter based on OS and architecture.
 
-                    else if (architecture == 'arm64' && os != 'Windows_NT') {
-                        affinityOptions = [
-                            "large_pages" : false
-                        ]
-                    }
+    switch (architecture) {
+        case 'arm64':
+            if (os != "Ubuntu" && os != "Windows_NT") {
+                return false
+            }
+            break
+        case 'armlb':
+            if (os != 'Windows_NT') {
+                return false
+            }
+            // Do not create armlb windows jobs.
+            return false
+        case 'arm':
+            if (os != "Ubuntu" && os != "Windows_NT") {
+                return false
+            }
+            break
+        case 'x86':
+            if (os != "Ubuntu") {
+                return false
+            }
+            break
+        case 'x64':
+            if (!(os in Constants.crossList)) {
+                return false
+            }
+            if (os == "Windows_NT") {
+                return false
+            }
+            break
+        case 'armem':
+        case 'x86_arm_altjit':
+        case 'x64_arm64_altjit':
+            // No flow jobs
+            return false
+        default:
+            println("Unknown architecture: ${architecture}")
+            assert false
+            break
+    }
 
-                    setMachineAffinity(newJob, os, architecture, affinityOptions)
-                    Utilities.standardJobSetup(newJob, project, isPR, "*/${branch}")
+    def isNormalOrInnerloop = (scenario == 'innerloop' || scenario == 'normal')
 
-                    setJobTimeout(newJob, isPR, architecture, configuration, scenario, false)
+    // Filter based on scenario in OS.
 
-                    if (windowsArmJob != true) {
-                        Utilities.addXUnitDotNETResults(newJob, '**/coreclrtests.xml')
-                    }
-                    else {
-                        if (!isCoreFxScenario(scenario)) {
-                            Utilities.addArchival(newJob, "bin/tests/${osGroup}.${architecture}.${configuration}/Smarty.run.0/*.smrt", '', true, false)
+    if (os == 'Windows_NT') {
+        if (!isArmWindowsScenario(scenario)) {
+            return false
+        }
+    }
+    else {
+        // Non-Windows
+        if (architecture == 'arm64') {
+            if (!(scenario in Constants.validLinuxArm64Scenarios)) {
+                return false
+            }
+        }
+        else if (architecture == 'arm') {
+            if (!(scenario in Constants.validLinuxArmScenarios)) {
+                return false
+            }
+        }
+        else if (architecture == 'x86') {
+            // Linux/x86 only want innerloop and default test
+            if (!isNormalOrInnerloop) {
+                return false
+            }
+        }
+    }
 
-                            // Archive a ZIP file of the entire Smarty.run.0 directory. This is possibly a little too much,
-                            // but there is no easy way to only archive the HTML/TXT files of the failing tests, so we get
-                            // all the passing test info as well. Not necessarily a bad thing, but possibly somewhat large.
-                            Utilities.addArchival(newJob, "bin/tests/${osGroup}.${architecture}.${configuration}/Smarty.run.0.zip", '', true, false)
-                        }
-                    }
+    // For CentOS, we only want Checked/Release builds.
+    if (os == 'CentOS7.1') {
+        if (configuration != 'Checked' && configuration != 'Release') {
+            return false
+        }
+        if (!isNormalOrInnerloop && !isR2RScenario(scenario) && !isJitStressScenario(scenario)) {
+            return false
+        }
+    }
 
-                    // =============================================================================================
-                    // Create a build flow to join together the build and tests required to run this test.
-                    // =============================================================================================
+    // For RedHat and Debian, we only do Release builds.
+    else if (os == 'RHEL7.2' || os == 'Debian8.4') {
+        if (configuration != 'Release') {
+            return false
+        }
+        if (!isNormalOrInnerloop) {
+            return false
+        }
+    }
 
-                    // Windows CoreCLR build and Linux CoreCLR build (in parallel) ->
-                    // Linux CoreCLR test
-                    def flowJobName = getJobName(configuration, architecture, os, scenario, false) + "_flow"
-                    def fullTestJobName = projectFolder + '/' + newJob.name
-                    // Add a reference to the input jobs for report purposes
-                    JobReport.Report.addReference(inputCoreCLRBuildName)
-                    if (windowsArmJob != true) {
-                        JobReport.Report.addReference(inputWindowsTestsBuildName)
-                    }
-                    JobReport.Report.addReference(fullTestJobName)
-                    def newFlowJob = null
+    // Next, filter based on scenario.
 
-                    if (os == 'RHEL7.2' || os == 'Debian8.4') {
-                        // Do not create the flow job for RHEL jobs.
+    if (isJitStressScenario(scenario)) {
+        if (configuration != 'Checked') {
+            return false
+        }
+
+        // CoreFx JIT stress tests currently only implemented for Windows ARM.
+        if (isCoreFxScenario(scenario) && !( (architecture == 'arm') && (os == 'Windows_NT') )) {
+            return false
+        }
+    }
+    else if (isR2RBaselineScenario(scenario)) {
+        if (configuration != 'Checked' && configuration != 'Release') {
+            return false
+        }
+    }
+    else if (isR2RStressScenario(scenario)) {
+        if (configuration != 'Checked') {
+            return false
+        }
+    }
+    else {
+        // Skip scenarios
+        switch (scenario) {
+            case 'ilrt':
+            case 'longgc':
+            case 'gcsimulator':
+                // Long GC tests take a long time on non-Release builds
+                // ilrt is also Release only
+                if (configuration != 'Release') {
+                    return false
+                }
+                break
+
+            case 'jitdiff':
+                if (configuration != 'Checked') {
+                    return false
+                }
+                break
+
+            case 'gc_reliability_framework':
+            case 'standalone_gc':
+                if (configuration != 'Release' && configuration != 'Checked') {
+                    return false
+                }
+                break
+
+            case 'formatting':
+                return false
+            case 'illink':
+                if (os != 'Windows_NT' && os != 'Ubuntu') {
+                    return false
+                }
+                break
+
+            case 'normal':
+                // Nothing skipped
+                break
+
+            case 'innerloop':
+                // Nothing skipped
+                if (!isValidPrTriggeredInnerLoopJob(os, architecture, configuration, false)) {
+                    return false
+                }
+                break
+
+            default:
+                println("Unknown scenario: ${scenario}")
+                assert false
+                break
+        }
+    }
+
+    // The job was not filtered out, so we should generate it!
+    return true
+}
+
+// Create jobs requiring flow jobs. This includes x64 non-Windows, arm/arm64 Ubuntu, and arm/arm64/armlb Windows.
+// Note: no armlb non-Windows; we expect to deprecate/remove armlb soon, so don't want to add new testing for it.
+Constants.allScenarios.each { scenario ->
+    [true, false].each { isPR ->
+        Constants.architectureList.each { architecture ->
+            Constants.configurationList.each { configuration ->
+                Constants.osList.each { os ->
+
+                    if (!shouldGenerateFlowJob(scenario, isPR, architecture, configuration, os)) {
                         return
                     }
-                    
-                    // For pri0 jobs we can build tests on unix
-                    if (windowsArmJob) {
-                        // For Windows arm jobs there is no reason to build a parallel test job.
-                        // The product build supports building and archiving the tests.
 
-                        newFlowJob = buildFlowJob(Utilities.getFullJobName(project, flowJobName, isPR, folder)) {
-                        buildFlow("""\
-coreclrBuildJob = build(params, '${inputCoreCLRBuildName}')
+                    // Figure out the job name of the CoreCLR build the test will depend on.
 
-// And then build the test build
-build(params + [CORECLR_BUILD: coreclrBuildJob.build.number], '${fullTestJobName}')
-""")
-                        }
+                    def inputCoreCLRBuildScenario = scenario == 'innerloop' ? 'innerloop' : 'normal'
+                    def inputCoreCLRBuildIsBuildOnly = false
+                    if (isCoreFxScenario(scenario)) {
+                        // Every CoreFx test depends on its own unique build.
+                        inputCoreCLRBuildScenario = scenario
+                        inputCoreCLRBuildIsBuildOnly = true
                     }
-                    else {
-                        newFlowJob = buildFlowJob(Utilities.getFullJobName(project, flowJobName, isPR, folder)) {
-                        buildFlow("""\
-// Build the input jobs in parallel
-parallel (
-{ coreclrBuildJob = build(params, '${inputCoreCLRBuildName}') },
-{ windowsBuildJob = build(params, '${inputWindowsTestsBuildName}') }
-)
+                    def inputCoreCLRFolderName = getJobFolder(inputCoreCLRBuildScenario)
+                    def inputCoreCLRBuildName = projectFolder + '/' +
+                        Utilities.getFullJobName(project, getJobName(configuration, architecture, os, inputCoreCLRBuildScenario, inputCoreCLRBuildIsBuildOnly), isPR, inputCoreCLRFolderName)
 
-// And then build the test build
-build(params + [CORECLR_BUILD: coreclrBuildJob.build.number,
-            CORECLR_WINDOWS_BUILD: windowsBuildJob.build.number], '${fullTestJobName}')
-""")
+                    // Figure out the name of the build job that the test job will depend on.
+                    // For Windows ARM tests, this is not used, as the CoreCLR build creates the tests. For other
+                    // tests (e.g., Linux ARM), we depend on a Windows build to get the tests.
+
+                    def inputTestsBuildName = null
+
+                    def windowsArmJob = ((os == "Windows_NT") && (architecture in Constants.armWindowsCrossArchitectureList))
+                    if (!windowsArmJob) {
+                        def testBuildScenario = scenario == 'innerloop' ? 'innerloop' : 'normal'
+
+                        def inputTestsBuildArch = architecture
+                        if (architecture == "arm64") {
+                            // Use the x64 test build for arm64 unix
+                            inputTestsBuildArch = "x64"
                         }
+                        else if (architecture == "arm") {
+                            // Use the x86 test build for arm unix
+                            inputTestsBuildArch = "x86"
+                        }
+
+                        def inputTestsBuildIsBuildOnly = true
+
+                        inputTestsBuildName = projectFolder + '/' +
+                            Utilities.getFullJobName(project, getJobName(configuration, inputTestsBuildArch, 'windows_nt', testBuildScenario, inputTestsBuildIsBuildOnly), isPR)
                     }
 
-                    addToViews(newFlowJob, isPR, architecture, os)
+                    // =============================================================================================
+                    // Create the test job
+                    // =============================================================================================
 
-                    // For the flow jobs set the machine affinity as x64 if an armarch.
-                    def flowArch = architecture
+                    def testJob = CreateTestJob(this, project, branch, architecture, os, configuration, scenario, isPR, inputCoreCLRBuildName, inputTestsBuildName)
 
-                    if (flowArch in validWindowsNTCrossArches) {
-                        flowArch = 'x64'
-                        affinityOptions = null
+                    // =============================================================================================
+                    // Create a build flow to join together the build and tests required to run this test.
+                    // =============================================================================================
+
+                    if (os == 'RHEL7.2' || os == 'Debian8.4') {
+                        // Do not create the flow job for RHEL jobs.
+                        return
                     }
 
-                    setMachineAffinity(newFlowJob, os, flowArch, affinityOptions)
-                    Utilities.standardJobSetup(newFlowJob, project, isPR, "*/${branch}")
-                    addTriggers(newFlowJob, branch, isPR, architecture, os, configuration, scenario, true, false) // isFlowJob==true, isWindowsBuildOnlyJob==false
-                } // configuration
-            } // os
+                    def fullTestJobName = projectFolder + '/' + testJob.name
+                    def flowJob = CreateFlowJob(this, project, branch, architecture, os, configuration, scenario, isPR, fullTestJobName, inputCoreCLRBuildName, inputTestsBuildName)
+
+                } // os
+            } // configuration
         } // architecture
     } // isPR
 } // scenario
index e1d40d6..b695abc 100644 (file)
@@ -253,12 +253,18 @@ inline uint8_t BitScanForward64(uint32_t *bitIndex, uint64_t mask)
     uint32_t hi = (mask >> 32) & 0xFFFFFFFF;
     uint32_t lo = mask & 0xFFFFFFFF;
     uint32_t fakeBitIndex = 0;
-    if (BitScanForward(&fakeBitIndex, hi))
+    
+    uint8_t result = BitScanForward(bitIndex, lo);
+    if (result == 0)
     {
-        *bitIndex = fakeBitIndex + 32;
+        result = BitScanForward(&fakeBitIndex, hi);
+        if (result != 0)
+        {
+            *bitIndex = fakeBitIndex + 32;
+        }
     }
 
-    return BitScanForward(bitIndex, lo);
+    return result;
  #else
     return _BitScanForward64((unsigned long*)bitIndex, mask);
  #endif // _WIN32
index c46cb8a..9437e60 100644 (file)
@@ -611,7 +611,7 @@ namespace ETW
 #else // FEATURE_EVENT_TRACE
     public:
         static VOID MethodJitting(MethodDesc *pMethodDesc, SString *namespaceOrClassName=NULL, SString *methodName=NULL, SString *methodSignature=NULL) {};
-        static VOID MethodJitted(MethodDesc *pMethodDesc, SString *namespaceOrClassName=NULL, SString *methodName=NULL, SString *methodSignature=NULL, SIZE_T pCode = 0, ReJITID rejitID = 0) {};
+        static VOID MethodJitted(MethodDesc *pMethodDesc, SString *namespaceOrClassName=NULL, SString *methodName=NULL, SString *methodSignature=NULL, SIZE_T pCode = 0, ReJITID rejitID = 0, BOOL bProfilerRejectedPrecompiledCode = FALSE, BOOL bReadyToRunRejectedPrecompiledCode = FALSE) {};
         static VOID StubInitialized(ULONGLONG ullHelperStartAddress, LPCWSTR pHelperName) {};
         static VOID StubsInitialized(PVOID *pHelperStartAddresss, PVOID *pHelperNames, LONG ulNoOfHelpers) {};
         static VOID MethodRestored(MethodDesc * pMethodDesc) {};
index ef823e6..de9ba01 100644 (file)
@@ -5317,7 +5317,7 @@ inline T* InterlockedExchangeT(
     std::nullptr_t value) // When nullptr is provided as argument.
 {
     //STATIC_ASSERT(value == 0);
-    return InterlockedExchangeT(target, reinterpret_cast<T*>(value));
+    return InterlockedExchangeT(target, static_cast<T*>(value));
 }
 
 template <typename T>
@@ -5327,7 +5327,7 @@ inline T* InterlockedCompareExchangeT(
     T*             comparand)
 {
     //STATIC_ASSERT(exchange == 0);
-    return InterlockedCompareExchangeT(destination, reinterpret_cast<T*>(exchange), comparand);
+    return InterlockedCompareExchangeT(destination, static_cast<T*>(exchange), comparand);
 }
 
 template <typename T>
@@ -5337,7 +5337,7 @@ inline T* InterlockedCompareExchangeT(
     std::nullptr_t comparand) // When nullptr is provided as argument.
 {
     //STATIC_ASSERT(comparand == 0);
-    return InterlockedCompareExchangeT(destination, exchange, reinterpret_cast<T*>(comparand));
+    return InterlockedCompareExchangeT(destination, exchange, static_cast<T*>(comparand));
 }
 
 #undef InterlockedExchangePointer
index 0c00e4a..9f7f38d 100644 (file)
@@ -6354,7 +6354,7 @@ void CodeGen::genIntToIntCast(GenTree* treeNode)
     bool      isUnsignedSrc = varTypeIsUnsigned(srcType);
 
     // if necessary, force the srcType to unsigned when the GT_UNSIGNED flag is set
-    if (!isUnsignedSrc && (treeNode->gtFlags & GTF_UNSIGNED) != 0)
+    if (!isUnsignedSrc && treeNode->IsUnsigned())
     {
         srcType       = genUnsignedType(srcType);
         isUnsignedSrc = true;
@@ -6371,37 +6371,29 @@ void CodeGen::genIntToIntCast(GenTree* treeNode)
 
     if (srcSize < dstSize)
     {
-        // Widening cast
-        // Is this an Overflow checking cast?
-        // We only need to handle one case, as the other casts can never overflow.
-        //   cast from TYP_INT to TYP_ULONG
-        //
-        if (treeNode->gtOverflow() && (srcType == TYP_INT) && (dstType == TYP_ULONG))
+#ifdef _TARGET_X86_
+        // dstType cannot be a long type on x86, such casts should have been decomposed.
+        // srcType cannot be a small type since it's the "actual type" of the cast operand.
+        // This means that widening casts do not actually occur on x86.
+        unreached();
+#else
+        // This is a widening cast from TYP_(U)INT to TYP_(U)LONG.
+        assert(dstSize == EA_8BYTE);
+        assert(srcSize == EA_4BYTE);
+
+        // When widening, overflows can only happen if the source type is signed and the
+        // destination type is unsigned. Since the overflow check ensures that the value
+        // is positive a cheaper mov instruction can be used instead of movsxd.
+        if (treeNode->gtOverflow() && !isUnsignedSrc && isUnsignedDst)
         {
             requiresOverflowCheck = true;
             ins                   = INS_mov;
         }
         else
         {
-            noway_assert(srcSize < EA_PTRSIZE);
-
-            ins = ins_Move_Extend(srcType, false);
-
-            /*
-                Special case: ins_Move_Extend assumes the destination type is no bigger
-                than TYP_INT.  movsx and movzx can already extend all the way to
-                64-bit, and a regular 32-bit mov clears the high 32 bits (like the non-existant movzxd),
-                but for a sign extension from TYP_INT to TYP_LONG, we need to use movsxd opcode.
-            */
-            if (!isUnsignedSrc && !isUnsignedDst)
-            {
-#ifdef _TARGET_X86_
-                NYI_X86("Cast to 64 bit for x86/RyuJIT");
-#else  // !_TARGET_X86_
-                ins = INS_movsxd;
-#endif // !_TARGET_X86_
-            }
+            ins = isUnsignedSrc ? INS_mov : INS_movsxd;
         }
+#endif
     }
     else
     {
index 54c1756..46eed69 100644 (file)
@@ -2128,9 +2128,9 @@ public:
 
     GenTree* gtUnusedValNode(GenTree* expr);
 
-    GenTree* gtNewCastNode(var_types typ, GenTree* op1, var_types castType);
+    GenTreeCast* gtNewCastNode(var_types typ, GenTree* op1, bool fromUnsigned, var_types castType);
 
-    GenTree* gtNewCastNodeL(var_types typ, GenTree* op1, var_types castType);
+    GenTreeCast* gtNewCastNodeL(var_types typ, GenTree* op1, bool fromUnsigned, var_types castType);
 
     GenTree* gtNewAllocObjNode(unsigned int helper, CORINFO_CLASS_HANDLE clsHnd, var_types type, GenTree* op1);
 
index e1f4482..ab3f32e 100644 (file)
@@ -1476,13 +1476,13 @@ inline void GenTree::SetOper(genTreeOps oper, ValueNumberUpdate vnUpdate)
     }
 }
 
-inline GenTree* Compiler::gtNewCastNode(var_types typ, GenTree* op1, var_types castType)
+inline GenTreeCast* Compiler::gtNewCastNode(var_types typ, GenTree* op1, bool fromUnsigned, var_types castType)
 {
-    GenTree* res = new (this, GT_CAST) GenTreeCast(typ, op1, castType);
+    GenTreeCast* res = new (this, GT_CAST) GenTreeCast(typ, op1, fromUnsigned, castType);
     return res;
 }
 
-inline GenTree* Compiler::gtNewCastNodeL(var_types typ, GenTree* op1, var_types castType)
+inline GenTreeCast* Compiler::gtNewCastNodeL(var_types typ, GenTree* op1, bool fromUnsigned, var_types castType)
 {
     /* Some casts get transformed into 'GT_CALL' or 'GT_IND' nodes */
 
@@ -1491,7 +1491,8 @@ inline GenTree* Compiler::gtNewCastNodeL(var_types typ, GenTree* op1, var_types
 
     /* Make a big node first and then change it to be GT_CAST */
 
-    GenTree* res = new (this, LargeOpOpcode()) GenTreeCast(typ, op1, castType DEBUGARG(/*largeNode*/ true));
+    GenTreeCast* res =
+        new (this, LargeOpOpcode()) GenTreeCast(typ, op1, fromUnsigned, castType DEBUGARG(/*largeNode*/ true));
     return res;
 }
 
index 5c8a7c9..ae5d8a7 100644 (file)
@@ -1924,12 +1924,7 @@ GenTree* DecomposeLongs::EnsureIntSized(GenTree* node, bool signExtend)
         return node;
     }
 
-    GenTree* const cast = m_compiler->gtNewCastNode(TYP_INT, node, node->TypeGet());
-    if (!signExtend)
-    {
-        cast->gtFlags |= GTF_UNSIGNED;
-    }
-
+    GenTree* const cast = m_compiler->gtNewCastNode(TYP_INT, node, !signExtend, node->TypeGet());
     Range().InsertAfter(node, cast);
     return cast;
 }
index 18b9bf4..68e67ca 100644 (file)
@@ -7468,7 +7468,7 @@ GenTree* Compiler::fgDoNormalizeOnStore(GenTree* tree)
 
                 if (fgCastNeeded(op2, varDsc->TypeGet()))
                 {
-                    op2              = gtNewCastNode(TYP_INT, op2, varDsc->TypeGet());
+                    op2              = gtNewCastNode(TYP_INT, op2, false, varDsc->TypeGet());
                     tree->gtOp.gtOp2 = op2;
 
                     // Propagate GTF_COLON_COND
@@ -20692,12 +20692,12 @@ Compiler::fgWalkResult Compiler::fgStress64RsltMulCB(GenTree** pTree, fgWalkData
 #endif // DEBUG
 
     // To ensure optNarrowTree() doesn't fold back to the original tree.
-    tree->gtOp.gtOp1 = pComp->gtNewCastNode(TYP_LONG, tree->gtOp.gtOp1, TYP_LONG);
+    tree->gtOp.gtOp1 = pComp->gtNewCastNode(TYP_LONG, tree->gtOp.gtOp1, false, TYP_LONG);
     tree->gtOp.gtOp1 = pComp->gtNewOperNode(GT_NOP, TYP_LONG, tree->gtOp.gtOp1);
-    tree->gtOp.gtOp1 = pComp->gtNewCastNode(TYP_LONG, tree->gtOp.gtOp1, TYP_LONG);
-    tree->gtOp.gtOp2 = pComp->gtNewCastNode(TYP_LONG, tree->gtOp.gtOp2, TYP_LONG);
+    tree->gtOp.gtOp1 = pComp->gtNewCastNode(TYP_LONG, tree->gtOp.gtOp1, false, TYP_LONG);
+    tree->gtOp.gtOp2 = pComp->gtNewCastNode(TYP_LONG, tree->gtOp.gtOp2, false, TYP_LONG);
     tree->gtType     = TYP_LONG;
-    *pTree           = pComp->gtNewCastNode(TYP_INT, tree, TYP_INT);
+    *pTree           = pComp->gtNewCastNode(TYP_INT, tree, false, TYP_INT);
 
 #ifdef DEBUG
     if (pComp->verbose)
index 47711e9..10ef373 100644 (file)
@@ -7820,8 +7820,9 @@ GenTree* Compiler::gtCloneExpr(
                 break;
 
             case GT_CAST:
-                copy = new (this, LargeOpOpcode()) GenTreeCast(tree->TypeGet(), tree->gtCast.CastOp(),
-                                                               tree->gtCast.gtCastType DEBUGARG(/*largeNode*/ TRUE));
+                copy =
+                    new (this, LargeOpOpcode()) GenTreeCast(tree->TypeGet(), tree->gtCast.CastOp(), tree->IsUnsigned(),
+                                                            tree->gtCast.gtCastType DEBUGARG(/*largeNode*/ TRUE));
                 break;
 
             // The nodes below this are not bashed, so they can be allocated at their individual sizes.
@@ -7993,10 +7994,6 @@ GenTree* Compiler::gtCloneExpr(
         {
             copy->gtFlags |= GTF_OVERFLOW;
         }
-        if (copy->OperGet() == GT_CAST)
-        {
-            copy->gtFlags |= (tree->gtFlags & GTF_UNSIGNED);
-        }
 
         if (tree->gtOp.gtOp1)
         {
@@ -14000,15 +13997,22 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree)
                                 goto CNS_INT;
 
                             case TYP_ULONG:
-                                if (!(tree->gtFlags & GTF_UNSIGNED) && tree->gtOverflow() && i1 < 0)
+                                if (tree->IsUnsigned())
                                 {
-                                    goto LNG_OVF;
+                                    lval1 = UINT64(UINT32(i1));
+                                }
+                                else
+                                {
+                                    if (tree->gtOverflow() && (i1 < 0))
+                                    {
+                                        goto LNG_OVF;
+                                    }
+                                    lval1 = UINT64(INT32(i1));
                                 }
-                                lval1 = UINT64(UINT32(i1));
                                 goto CNS_LONG;
 
                             case TYP_LONG:
-                                if (tree->gtFlags & GTF_UNSIGNED)
+                                if (tree->IsUnsigned())
                                 {
                                     lval1 = INT64(UINT32(i1));
                                 }
@@ -15431,11 +15435,11 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree*                objPtr,
             }
             else if (lclTyp == TYP_DOUBLE && assg->TypeGet() == TYP_FLOAT)
             {
-                assg = gtNewCastNode(TYP_DOUBLE, assg, TYP_DOUBLE);
+                assg = gtNewCastNode(TYP_DOUBLE, assg, false, TYP_DOUBLE);
             }
             else if (lclTyp == TYP_FLOAT && assg->TypeGet() == TYP_DOUBLE)
             {
-                assg = gtNewCastNode(TYP_FLOAT, assg, TYP_FLOAT);
+                assg = gtNewCastNode(TYP_FLOAT, assg, false, TYP_FLOAT);
             }
 
             args       = gtNewArgList(assg);
@@ -15495,7 +15499,7 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree*                objPtr,
             else if (varTypeIsIntegral(lclTyp) && genTypeSize(lclTyp) < genTypeSize(TYP_INT))
             {
                 // The helper does not extend the small return types.
-                tree = gtNewCastNode(genActualType(lclTyp), tree, lclTyp);
+                tree = gtNewCastNode(genActualType(lclTyp), tree, false, lclTyp);
             }
         }
     }
index c33cb51..4ed7cf0 100644 (file)
@@ -3031,9 +3031,10 @@ struct GenTreeCast : public GenTreeOp
     }
     var_types gtCastType;
 
-    GenTreeCast(var_types type, GenTree* op, var_types castType DEBUGARG(bool largeNode = false))
+    GenTreeCast(var_types type, GenTree* op, bool fromUnsigned, var_types castType DEBUGARG(bool largeNode = false))
         : GenTreeOp(GT_CAST, type, op, nullptr DEBUGARG(largeNode)), gtCastType(castType)
     {
+        gtFlags |= fromUnsigned ? GTF_UNSIGNED : 0;
     }
 #if DEBUGGABLE_GENTREE
     GenTreeCast() : GenTreeOp()
index b7a769a..3347966 100644 (file)
@@ -167,7 +167,7 @@ HARDWARE_INTRINSIC(SSE2_CompareOrdered,                              "CompareOrd
 HARDWARE_INTRINSIC(SSE2_CompareOrderedScalar,                        "CompareOrderedScalar",                             SSE2,        7,           16,          2,            {INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_cmpsd},             HW_Category_SIMDScalar,                        HW_Flag_CopyUpperBits)
 HARDWARE_INTRINSIC(SSE2_CompareUnordered,                            "CompareUnordered",                                 SSE2,        3,           16,          2,            {INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_cmppd},             HW_Category_SimpleSIMD,                        HW_Flag_NoFlag)
 HARDWARE_INTRINSIC(SSE2_CompareUnorderedScalar,                      "CompareUnorderedScalar",                           SSE2,        3,           16,          2,            {INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_cmpsd},             HW_Category_SIMDScalar,                        HW_Flag_CopyUpperBits)
-HARDWARE_INTRINSIC(SSE2_ConvertToDouble,                             "ConvertToDouble",                                  SSE2,       -1,           16,          1,            {INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_movsd},             HW_Category_Helper,                            HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoRMWSemantics)
+HARDWARE_INTRINSIC(SSE2_ConvertToDouble,                             "ConvertToDouble",                                  SSE2,       -1,           16,          1,            {INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_movsdsse2},         HW_Category_Helper,                            HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoRMWSemantics)
 HARDWARE_INTRINSIC(SSE2_ConvertToInt32,                              "ConvertToInt32",                                   SSE2,       -1,           16,          1,            {INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_mov_xmm2i, INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_cvtsd2si},          HW_Category_SIMDScalar,                        HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_NoRMWSemantics)
 HARDWARE_INTRINSIC(SSE2_ConvertToInt32WithTruncation,                "ConvertToInt32WithTruncation",                     SSE2,       -1,           16,          1,            {INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_cvttsd2si},         HW_Category_SIMDScalar,                        HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_NoRMWSemantics)
 HARDWARE_INTRINSIC(SSE2_ConvertToInt64,                              "ConvertToInt64",                                   SSE2,       -1,           16,          1,            {INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_invalid,   INS_mov_xmm2i, INS_invalid,   INS_invalid,   INS_cvtsd2si},          HW_Category_SIMDScalar,                        HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_NoRMWSemantics)
index bee8038..e870653 100644 (file)
@@ -901,11 +901,11 @@ GenTreeArgList* Compiler::impPopList(unsigned count, CORINFO_SIG_INFO* sig, GenT
 
             if (corType == CORINFO_TYPE_DOUBLE && args->Current()->TypeGet() == TYP_FLOAT)
             {
-                args->Current() = gtNewCastNode(TYP_DOUBLE, args->Current(), TYP_DOUBLE);
+                args->Current() = gtNewCastNode(TYP_DOUBLE, args->Current(), false, TYP_DOUBLE);
             }
             else if (corType == CORINFO_TYPE_FLOAT && args->Current()->TypeGet() == TYP_DOUBLE)
             {
-                args->Current() = gtNewCastNode(TYP_FLOAT, args->Current(), TYP_FLOAT);
+                args->Current() = gtNewCastNode(TYP_FLOAT, args->Current(), false, TYP_FLOAT);
             }
 
             // insert any widening or narrowing casts for backwards compatibility
@@ -2895,12 +2895,12 @@ GenTree* Compiler::impImplicitIorI4Cast(GenTree* tree, var_types dstTyp)
         else if (varTypeIsI(wantedType) && (currType == TYP_INT))
         {
             // Note that this allows TYP_INT to be cast to a TYP_I_IMPL when wantedType is a TYP_BYREF or TYP_REF
-            tree = gtNewCastNode(TYP_I_IMPL, tree, TYP_I_IMPL);
+            tree = gtNewCastNode(TYP_I_IMPL, tree, false, TYP_I_IMPL);
         }
         else if ((wantedType == TYP_INT) && varTypeIsI(currType))
         {
             // Note that this allows TYP_BYREF or TYP_REF to be cast to a TYP_INT
-            tree = gtNewCastNode(TYP_INT, tree, TYP_INT);
+            tree = gtNewCastNode(TYP_INT, tree, false, TYP_INT);
         }
 #endif // _TARGET_64BIT_
     }
@@ -2919,7 +2919,7 @@ GenTree* Compiler::impImplicitR4orR8Cast(GenTree* tree, var_types dstTyp)
 #ifndef LEGACY_BACKEND
     if (varTypeIsFloating(tree) && varTypeIsFloating(dstTyp) && (dstTyp != tree->gtType))
     {
-        tree = gtNewCastNode(dstTyp, tree, dstTyp);
+        tree = gtNewCastNode(dstTyp, tree, false, dstTyp);
     }
 #endif // !LEGACY_BACKEND
 
@@ -4011,10 +4011,11 @@ GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method,
                 noway_assert(varTypeIsFloating(op1));
 
 #else // FEATURE_X87_DOUBLES
+                assert(varTypeIsFloating(op1));
 
                 if (op1->TypeGet() != callType)
                 {
-                    op1 = gtNewCastNode(callType, op1, callType);
+                    op1 = gtNewCastNode(callType, op1, false, callType);
                 }
 
 #endif // FEATURE_X87_DOUBLES
@@ -4035,14 +4036,16 @@ GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method,
                 noway_assert(varTypeIsFloating(op1));
 
 #else // FEATURE_X87_DOUBLES
+                assert(varTypeIsFloating(op1));
+                assert(varTypeIsFloating(op2));
 
                 if (op2->TypeGet() != callType)
                 {
-                    op2 = gtNewCastNode(callType, op2, callType);
+                    op2 = gtNewCastNode(callType, op2, false, callType);
                 }
                 if (op1->TypeGet() != callType)
                 {
-                    op1 = gtNewCastNode(callType, op1, callType);
+                    op1 = gtNewCastNode(callType, op1, false, callType);
                 }
 
 #endif // FEATURE_X87_DOUBLES
@@ -5716,7 +5719,7 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken)
             {
                 assert((varTypeIsFloating(srcTyp) && varTypeIsFloating(dstTyp)) ||
                        (varTypeIsIntegral(srcTyp) && varTypeIsIntegral(dstTyp)));
-                exprToBox = gtNewCastNode(dstTyp, exprToBox, dstTyp);
+                exprToBox = gtNewCastNode(dstTyp, exprToBox, false, dstTyp);
             }
             op1 = gtNewAssignNode(gtNewOperNode(GT_IND, lclTyp, op1), exprToBox);
         }
@@ -8346,7 +8349,7 @@ DONE_CALL:
 
             if (checkForSmallType && varTypeIsIntegral(callRetTyp) && genTypeSize(callRetTyp) < genTypeSize(TYP_INT))
             {
-                call = gtNewCastNode(genActualType(callRetTyp), call, callRetTyp);
+                call = gtNewCastNode(genActualType(callRetTyp), call, false, callRetTyp);
             }
         }
 
@@ -9686,7 +9689,7 @@ var_types Compiler::impGetByRefResultType(genTreeOps oper, bool fUnsigned, GenTr
             if (genActualType(op1->TypeGet()) != TYP_I_IMPL)
             {
                 // insert an explicit upcast
-                op1 = *pOp1 = gtNewCastNode(TYP_I_IMPL, op1, (var_types)(fUnsigned ? TYP_U_IMPL : TYP_I_IMPL));
+                op1 = *pOp1 = gtNewCastNode(TYP_I_IMPL, op1, fUnsigned, fUnsigned ? TYP_U_IMPL : TYP_I_IMPL);
             }
 #endif // _TARGET_64BIT_
 
@@ -9701,7 +9704,7 @@ var_types Compiler::impGetByRefResultType(genTreeOps oper, bool fUnsigned, GenTr
             if ((genActualType(op2->TypeGet()) != TYP_I_IMPL))
             {
                 // insert an explicit upcast
-                op2 = *pOp2 = gtNewCastNode(TYP_I_IMPL, op2, (var_types)(fUnsigned ? TYP_U_IMPL : TYP_I_IMPL));
+                op2 = *pOp2 = gtNewCastNode(TYP_I_IMPL, op2, fUnsigned, fUnsigned ? TYP_U_IMPL : TYP_I_IMPL);
             }
 #endif // _TARGET_64BIT_
 
@@ -9725,13 +9728,13 @@ var_types Compiler::impGetByRefResultType(genTreeOps oper, bool fUnsigned, GenTr
             if (genActualType(op1->TypeGet()) != TYP_I_IMPL)
             {
                 // insert an explicit upcast
-                op1 = *pOp1 = gtNewCastNode(TYP_I_IMPL, op1, (var_types)(fUnsigned ? TYP_U_IMPL : TYP_I_IMPL));
+                op1 = *pOp1 = gtNewCastNode(TYP_I_IMPL, op1, fUnsigned, fUnsigned ? TYP_U_IMPL : TYP_I_IMPL);
             }
         }
         else if (genActualType(op2->TypeGet()) != TYP_I_IMPL)
         {
             // insert an explicit upcast
-            op2 = *pOp2 = gtNewCastNode(TYP_I_IMPL, op2, (var_types)(fUnsigned ? TYP_U_IMPL : TYP_I_IMPL));
+            op2 = *pOp2 = gtNewCastNode(TYP_I_IMPL, op2, fUnsigned, fUnsigned ? TYP_U_IMPL : TYP_I_IMPL);
         }
 #endif // _TARGET_64BIT_
 
@@ -9749,12 +9752,12 @@ var_types Compiler::impGetByRefResultType(genTreeOps oper, bool fUnsigned, GenTr
         if (genActualType(op1->TypeGet()) != TYP_I_IMPL)
         {
             // insert an explicit upcast
-            op1 = *pOp1 = gtNewCastNode(TYP_I_IMPL, op1, (var_types)(fUnsigned ? TYP_U_IMPL : TYP_I_IMPL));
+            op1 = *pOp1 = gtNewCastNode(TYP_I_IMPL, op1, fUnsigned, fUnsigned ? TYP_U_IMPL : TYP_I_IMPL);
         }
         else if (genActualType(op2->TypeGet()) != TYP_I_IMPL)
         {
             // insert an explicit upcast
-            op2 = *pOp2 = gtNewCastNode(TYP_I_IMPL, op2, (var_types)(fUnsigned ? TYP_U_IMPL : TYP_I_IMPL));
+            op2 = *pOp2 = gtNewCastNode(TYP_I_IMPL, op2, fUnsigned, fUnsigned ? TYP_U_IMPL : TYP_I_IMPL);
         }
 
         type = TYP_I_IMPL;
@@ -10710,7 +10713,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                 if (varTypeIsI(op1->TypeGet()) && (genActualType(lclTyp) == TYP_INT))
                 {
                     assert(!tiVerificationNeeded); // We should have thrown the VerificationException before.
-                    op1 = gtNewCastNode(TYP_INT, op1, TYP_INT);
+                    op1 = gtNewCastNode(TYP_INT, op1, false, TYP_INT);
                 }
 #endif // _TARGET_64BIT_
 
@@ -10801,7 +10804,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                 if ((op1->TypeGet() != op2->TypeGet()) && varTypeIsFloating(op1->gtType) &&
                     varTypeIsFloating(op2->gtType))
                 {
-                    op1 = gtNewCastNode(op2->TypeGet(), op1, op2->TypeGet());
+                    op1 = gtNewCastNode(op2->TypeGet(), op1, false, op2->TypeGet());
                 }
 #endif // !FEATURE_X87_DOUBLES
 
@@ -11738,12 +11741,12 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                     if (op1->TypeGet() != type)
                     {
                         // We insert a cast of op1 to 'type'
-                        op1 = gtNewCastNode(type, op1, type);
+                        op1 = gtNewCastNode(type, op1, false, type);
                     }
                     if (op2->TypeGet() != type)
                     {
                         // We insert a cast of op2 to 'type'
-                        op2 = gtNewCastNode(type, op2, type);
+                        op2 = gtNewCastNode(type, op2, false, type);
                     }
                 }
 #endif // !FEATURE_X87_DOUBLES
@@ -12036,11 +12039,11 @@ void Compiler::impImportBlockCode(BasicBlock* block)
 #ifdef _TARGET_64BIT_
                 if (varTypeIsI(op1->TypeGet()) && (genActualType(op2->TypeGet()) == TYP_INT))
                 {
-                    op2 = gtNewCastNode(TYP_I_IMPL, op2, (var_types)(uns ? TYP_U_IMPL : TYP_I_IMPL));
+                    op2 = gtNewCastNode(TYP_I_IMPL, op2, uns, uns ? TYP_U_IMPL : TYP_I_IMPL);
                 }
                 else if (varTypeIsI(op2->TypeGet()) && (genActualType(op1->TypeGet()) == TYP_INT))
                 {
-                    op1 = gtNewCastNode(TYP_I_IMPL, op1, (var_types)(uns ? TYP_U_IMPL : TYP_I_IMPL));
+                    op1 = gtNewCastNode(TYP_I_IMPL, op1, uns, uns ? TYP_U_IMPL : TYP_I_IMPL);
                 }
 #endif // _TARGET_64BIT_
 
@@ -12136,11 +12139,11 @@ void Compiler::impImportBlockCode(BasicBlock* block)
 #ifdef _TARGET_64BIT_
                 if ((op1->TypeGet() == TYP_I_IMPL) && (genActualType(op2->TypeGet()) == TYP_INT))
                 {
-                    op2 = gtNewCastNode(TYP_I_IMPL, op2, (var_types)(uns ? TYP_U_IMPL : TYP_I_IMPL));
+                    op2 = gtNewCastNode(TYP_I_IMPL, op2, uns, uns ? TYP_U_IMPL : TYP_I_IMPL);
                 }
                 else if ((op2->TypeGet() == TYP_I_IMPL) && (genActualType(op1->TypeGet()) == TYP_INT))
                 {
-                    op1 = gtNewCastNode(TYP_I_IMPL, op1, (var_types)(uns ? TYP_U_IMPL : TYP_I_IMPL));
+                    op1 = gtNewCastNode(TYP_I_IMPL, op1, uns, uns ? TYP_U_IMPL : TYP_I_IMPL);
                 }
 #endif // _TARGET_64BIT_
 
@@ -12189,12 +12192,12 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                         if (op1->TypeGet() == TYP_DOUBLE)
                         {
                             // We insert a cast of op2 to TYP_DOUBLE
-                            op2 = gtNewCastNode(TYP_DOUBLE, op2, TYP_DOUBLE);
+                            op2 = gtNewCastNode(TYP_DOUBLE, op2, false, TYP_DOUBLE);
                         }
                         else if (op2->TypeGet() == TYP_DOUBLE)
                         {
                             // We insert a cast of op1 to TYP_DOUBLE
-                            op1 = gtNewCastNode(TYP_DOUBLE, op1, TYP_DOUBLE);
+                            op1 = gtNewCastNode(TYP_DOUBLE, op1, false, TYP_DOUBLE);
                         }
                     }
                 }
@@ -12485,22 +12488,18 @@ void Compiler::impImportBlockCode(BasicBlock* block)
 #if SMALL_TREE_NODES
                 if (callNode)
                 {
-                    op1 = gtNewCastNodeL(type, op1, lclTyp);
+                    op1 = gtNewCastNodeL(type, op1, uns, lclTyp);
                 }
                 else
 #endif // SMALL_TREE_NODES
                 {
-                    op1 = gtNewCastNode(type, op1, lclTyp);
+                    op1 = gtNewCastNode(type, op1, uns, lclTyp);
                 }
 
                 if (ovfl)
                 {
                     op1->gtFlags |= (GTF_OVERFLOW | GTF_EXCEPT);
                 }
-                if (uns)
-                {
-                    op1->gtFlags |= GTF_UNSIGNED;
-                }
                 impPushOnStack(op1, tiRetVal);
                 break;
 
@@ -12683,14 +12682,14 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                     if (varTypeIsI(op2->gtType) && (genActualType(lclTyp) == TYP_INT))
                     {
                         assert(!tiVerificationNeeded); // We should have thrown the VerificationException before.
-                        op2 = gtNewCastNode(TYP_INT, op2, TYP_INT);
+                        op2 = gtNewCastNode(TYP_INT, op2, false, TYP_INT);
                     }
                     // Allow an upcast of op2 from a 32-bit Int into TYP_I_IMPL for x86 JIT compatiblity
                     //
                     if (varTypeIsI(lclTyp) && (genActualType(op2->gtType) == TYP_INT))
                     {
                         assert(!tiVerificationNeeded); // We should have thrown the VerificationException before.
-                        op2 = gtNewCastNode(TYP_I_IMPL, op2, TYP_I_IMPL);
+                        op2 = gtNewCastNode(TYP_I_IMPL, op2, false, TYP_I_IMPL);
                     }
                 }
 #endif // _TARGET_64BIT_
@@ -12814,7 +12813,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                 if (genActualType(op1->gtType) == TYP_INT)
                 {
                     assert(!tiVerificationNeeded); // We should have thrown the VerificationException before.
-                    op1 = gtNewCastNode(TYP_I_IMPL, op1, TYP_I_IMPL);
+                    op1 = gtNewCastNode(TYP_I_IMPL, op1, false, TYP_I_IMPL);
                 }
 #endif
 
@@ -14149,7 +14148,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                     if ((op1->TypeGet() != op2->TypeGet()) && op2->OperIsConst() && varTypeIsIntOrI(op2->TypeGet()) &&
                         varTypeIsLong(op1->TypeGet()))
                     {
-                        op2 = gtNewCastNode(op1->TypeGet(), op2, op1->TypeGet());
+                        op2 = gtNewCastNode(op1->TypeGet(), op2, false, op1->TypeGet());
                     }
 #endif
 
@@ -14165,13 +14164,13 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                         //
                         if (varTypeIsI(op2->gtType) && (genActualType(lclTyp) == TYP_INT))
                         {
-                            op2 = gtNewCastNode(TYP_INT, op2, TYP_INT);
+                            op2 = gtNewCastNode(TYP_INT, op2, false, TYP_INT);
                         }
                         // Allow an upcast of op2 from a 32-bit Int into TYP_I_IMPL for x86 JIT compatiblity
                         //
                         if (varTypeIsI(lclTyp) && (genActualType(op2->gtType) == TYP_INT))
                         {
-                            op2 = gtNewCastNode(TYP_I_IMPL, op2, TYP_I_IMPL);
+                            op2 = gtNewCastNode(TYP_I_IMPL, op2, false, TYP_I_IMPL);
                         }
                     }
 #endif
@@ -14183,7 +14182,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                     if ((op1->TypeGet() != op2->TypeGet()) && varTypeIsFloating(op1->gtType) &&
                         varTypeIsFloating(op2->gtType))
                     {
-                        op2 = gtNewCastNode(op1->TypeGet(), op2, op1->TypeGet());
+                        op2 = gtNewCastNode(op1->TypeGet(), op2, false, op1->TypeGet());
                     }
 #endif // !FEATURE_X87_DOUBLES
 
@@ -15857,7 +15856,7 @@ bool Compiler::impReturnInstruction(BasicBlock* block, int prefixFlags, OPCODE&
                         fgCastNeeded(op2, fncRealRetType))
                     {
                         // Small-typed return values are normalized by the callee
-                        op2 = gtNewCastNode(TYP_INT, op2, fncRealRetType);
+                        op2 = gtNewCastNode(TYP_INT, op2, false, fncRealRetType);
                     }
                 }
 
@@ -16646,7 +16645,7 @@ SPILLSTACK:
             {
                 // Spill clique has decided this should be "native int", but this block only pushes an "int".
                 // Insert a sign-extension to "native int" so we match the clique.
-                verCurrentState.esStack[level].val = gtNewCastNode(TYP_I_IMPL, tree, TYP_I_IMPL);
+                verCurrentState.esStack[level].val = gtNewCastNode(TYP_I_IMPL, tree, false, TYP_I_IMPL);
             }
 
             // Consider the case where one branch left a 'byref' on the stack and the other leaves
@@ -16670,7 +16669,7 @@ SPILLSTACK:
                 {
                     // Spill clique has decided this should be "byref", but this block only pushes an "int".
                     // Insert a sign-extension to "native int" so we match the clique size.
-                    verCurrentState.esStack[level].val = gtNewCastNode(TYP_I_IMPL, tree, TYP_I_IMPL);
+                    verCurrentState.esStack[level].val = gtNewCastNode(TYP_I_IMPL, tree, false, TYP_I_IMPL);
                 }
             }
 #endif // _TARGET_64BIT_
@@ -16699,7 +16698,7 @@ SPILLSTACK:
             {
                 // Spill clique has decided this should be "double", but this block only pushes a "float".
                 // Insert a cast to "double" so we match the clique.
-                verCurrentState.esStack[level].val = gtNewCastNode(TYP_DOUBLE, tree, TYP_DOUBLE);
+                verCurrentState.esStack[level].val = gtNewCastNode(TYP_DOUBLE, tree, false, TYP_DOUBLE);
             }
 
 #endif // FEATURE_X87_DOUBLES
@@ -18420,7 +18419,7 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo)
                         continue;
                     }
 
-                    inlArgNode = inlArgInfo[i].argNode = gtNewCastNode(TYP_INT, inlArgNode, sigType);
+                    inlArgNode = inlArgInfo[i].argNode = gtNewCastNode(TYP_INT, inlArgNode, false, sigType);
 
                     inlArgInfo[i].argIsLclVar = false;
 
@@ -18437,7 +18436,8 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo)
                 else if (genTypeSize(genActualType(inlArgNode->gtType)) < genTypeSize(sigType))
                 {
                     // This should only happen for int -> native int widening
-                    inlArgNode = inlArgInfo[i].argNode = gtNewCastNode(genActualType(sigType), inlArgNode, sigType);
+                    inlArgNode = inlArgInfo[i].argNode =
+                        gtNewCastNode(genActualType(sigType), inlArgNode, false, sigType);
 
                     inlArgInfo[i].argIsLclVar = false;
 
index 941480b..dbac32d 100644 (file)
@@ -748,8 +748,7 @@ GenTree* Lowering::LowerSwitch(GenTree* node)
             {
                 // SWITCH_TABLE expects the switch value (the index into the jump table) to be TYP_I_IMPL.
                 // Note that the switch value is unsigned so the cast should be unsigned as well.
-                switchValue = comp->gtNewCastNode(TYP_I_IMPL, switchValue, TYP_U_IMPL);
-                switchValue->gtFlags |= GTF_UNSIGNED;
+                switchValue = comp->gtNewCastNode(TYP_I_IMPL, switchValue, true, TYP_U_IMPL);
                 switchBlockRange.InsertAtEnd(switchValue);
             }
 #endif
index 85e615c..0d755fe 100644 (file)
@@ -422,8 +422,8 @@ void Lowering::LowerCast(GenTree* tree)
 
     if (tmpType != TYP_UNDEF)
     {
-        GenTree* tmp = comp->gtNewCastNode(tmpType, op1, tmpType);
-        tmp->gtFlags |= (tree->gtFlags & (GTF_UNSIGNED | GTF_OVERFLOW | GTF_EXCEPT));
+        GenTree* tmp = comp->gtNewCastNode(tmpType, op1, tree->IsUnsigned(), tmpType);
+        tmp->gtFlags |= (tree->gtFlags & (GTF_OVERFLOW | GTF_EXCEPT));
 
         tree->gtFlags &= ~GTF_UNSIGNED;
         tree->gtOp.gtOp1 = tmp;
index 0aa5693..8216515 100644 (file)
@@ -748,8 +748,8 @@ void Lowering::LowerCast(GenTree* tree)
 
     if (tmpType != TYP_UNDEF)
     {
-        GenTree* tmp = comp->gtNewCastNode(tmpType, castOp, tmpType);
-        tmp->gtFlags |= (tree->gtFlags & (GTF_UNSIGNED | GTF_OVERFLOW | GTF_EXCEPT));
+        GenTree* tmp = comp->gtNewCastNode(tmpType, castOp, tree->IsUnsigned(), tmpType);
+        tmp->gtFlags |= (tree->gtFlags & (GTF_OVERFLOW | GTF_EXCEPT));
 
         tree->gtFlags &= ~GTF_UNSIGNED;
         tree->gtOp.gtOp1 = tmp;
index 8c9ca51..22d6b61 100644 (file)
@@ -191,7 +191,7 @@ GenTree* Compiler::fgMorphCast(GenTree* tree)
 #endif // FEATURE_STACK_FP_X87
                 )
         {
-            oper = gtNewCastNode(TYP_DOUBLE, oper, TYP_DOUBLE);
+            oper = gtNewCastNode(TYP_DOUBLE, oper, false, TYP_DOUBLE);
         }
 
         // do we need to do it in two steps R -> I, '-> smallType
@@ -200,14 +200,14 @@ GenTree* Compiler::fgMorphCast(GenTree* tree)
 #if defined(_TARGET_ARM64_) || defined(_TARGET_AMD64_)
         if (dstSize < genTypeSize(TYP_INT))
         {
-            oper = gtNewCastNodeL(TYP_INT, oper, TYP_INT);
-            oper->gtFlags |= (tree->gtFlags & (GTF_UNSIGNED | GTF_OVERFLOW | GTF_EXCEPT));
+            oper = gtNewCastNodeL(TYP_INT, oper, tree->IsUnsigned(), TYP_INT);
+            oper->gtFlags |= (tree->gtFlags & (GTF_OVERFLOW | GTF_EXCEPT));
             tree->gtFlags &= ~GTF_UNSIGNED;
         }
 #else
         if (dstSize < TARGET_POINTER_SIZE)
         {
-            oper = gtNewCastNodeL(TYP_I_IMPL, oper, TYP_I_IMPL);
+            oper = gtNewCastNodeL(TYP_I_IMPL, oper, false, TYP_I_IMPL);
             oper->gtFlags |= (tree->gtFlags & (GTF_OVERFLOW | GTF_EXCEPT));
         }
 #endif
@@ -293,8 +293,8 @@ GenTree* Compiler::fgMorphCast(GenTree* tree)
     // intermediate cast to native int.
     else if (varTypeIsLong(srcType) && varTypeIsSmall(dstType))
     {
-        oper = gtNewCastNode(TYP_I_IMPL, oper, TYP_I_IMPL);
-        oper->gtFlags |= (tree->gtFlags & (GTF_OVERFLOW | GTF_EXCEPT | GTF_UNSIGNED));
+        oper = gtNewCastNode(TYP_I_IMPL, oper, tree->IsUnsigned(), TYP_I_IMPL);
+        oper->gtFlags |= (tree->gtFlags & (GTF_OVERFLOW | GTF_EXCEPT));
         tree->gtFlags &= ~GTF_UNSIGNED;
     }
 #endif //!_TARGET_64BIT_
@@ -322,7 +322,7 @@ GenTree* Compiler::fgMorphCast(GenTree* tree)
             tree->CastToType() = TYP_DOUBLE;
             tree->gtType       = TYP_DOUBLE;
 
-            tree = gtNewCastNode(TYP_FLOAT, tree, TYP_FLOAT);
+            tree = gtNewCastNode(TYP_FLOAT, tree, false, TYP_FLOAT);
 
             return fgMorphTree(tree);
         }
@@ -341,7 +341,7 @@ GenTree* Compiler::fgMorphCast(GenTree* tree)
     // The following conversions are performed as two-step operations using above.
     // U4 -> R4/8 = U4-> Long -> R4/8
     // U8 -> R4   = U8 -> R8 -> R4
-    else if ((tree->gtFlags & GTF_UNSIGNED) && varTypeIsFloating(dstType))
+    else if (tree->IsUnsigned() && varTypeIsFloating(dstType))
     {
         srcType = genUnsignedType(srcType);
 
@@ -356,14 +356,14 @@ GenTree* Compiler::fgMorphCast(GenTree* tree)
                 // - recurse into the resulting tree
                 tree->CastToType() = TYP_DOUBLE;
                 tree->gtType       = TYP_DOUBLE;
-                tree               = gtNewCastNode(TYP_FLOAT, tree, TYP_FLOAT);
+                tree               = gtNewCastNode(TYP_FLOAT, tree, false, TYP_FLOAT);
                 return fgMorphTree(tree);
             }
         }
         else if (srcType == TYP_UINT)
         {
-            oper = gtNewCastNode(TYP_LONG, oper, TYP_LONG);
-            oper->gtFlags |= (tree->gtFlags & (GTF_OVERFLOW | GTF_EXCEPT | GTF_UNSIGNED));
+            oper = gtNewCastNode(TYP_LONG, oper, true, TYP_LONG);
+            oper->gtFlags |= (tree->gtFlags & (GTF_OVERFLOW | GTF_EXCEPT));
             tree->gtFlags &= ~GTF_UNSIGNED;
         }
     }
@@ -371,7 +371,7 @@ GenTree* Compiler::fgMorphCast(GenTree* tree)
 
 #ifdef _TARGET_X86_
     // Do we have to do two step U4/8 -> R4/8 ?
-    else if ((tree->gtFlags & GTF_UNSIGNED) && varTypeIsFloating(dstType))
+    else if (tree->IsUnsigned() && varTypeIsFloating(dstType))
     {
         srcType = genUnsignedType(srcType);
 
@@ -381,8 +381,8 @@ GenTree* Compiler::fgMorphCast(GenTree* tree)
         }
         else if (srcType == TYP_UINT)
         {
-            oper = gtNewCastNode(TYP_LONG, oper, TYP_LONG);
-            oper->gtFlags |= (tree->gtFlags & (GTF_OVERFLOW | GTF_EXCEPT | GTF_UNSIGNED));
+            oper = gtNewCastNode(TYP_LONG, oper, true, TYP_LONG);
+            oper->gtFlags |= (tree->gtFlags & (GTF_OVERFLOW | GTF_EXCEPT));
             tree->gtFlags &= ~GTF_UNSIGNED;
 #ifndef LEGACY_BACKEND
             return fgMorphCastIntoHelper(tree, CORINFO_HELP_LNG2DBL, oper);
@@ -413,7 +413,7 @@ GenTree* Compiler::fgMorphCast(GenTree* tree)
         oper->gtType    = srcType;
 
         // do the real cast
-        GenTree* cast = gtNewCastNode(tree->TypeGet(), gtNewLclvNode(lclNum, TYP_I_IMPL), dstType);
+        GenTree* cast = gtNewCastNode(tree->TypeGet(), gtNewLclvNode(lclNum, TYP_I_IMPL), false, dstType);
 
         // Generate the comma tree
         oper = gtNewOperNode(GT_COMMA, tree->TypeGet(), asg, cast);
@@ -537,10 +537,10 @@ GenTree* Compiler::fgMorphCast(GenTree* tree)
                 DEBUG_DESTROY_NODE(tree);
 
                 // Insert narrowing casts for op1 and op2
-                oper->gtOp.gtOp1 = gtNewCastNode(TYP_INT, oper->gtOp.gtOp1, dstType);
+                oper->gtOp.gtOp1 = gtNewCastNode(TYP_INT, oper->gtOp.gtOp1, false, dstType);
                 if (oper->gtOp.gtOp2 != nullptr)
                 {
-                    oper->gtOp.gtOp2 = gtNewCastNode(TYP_INT, oper->gtOp.gtOp2, dstType);
+                    oper->gtOp.gtOp2 = gtNewCastNode(TYP_INT, oper->gtOp.gtOp2, false, dstType);
                 }
 
                 // Clear the GT_MUL_64RSLT if it is set
@@ -6162,7 +6162,7 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
 
         if (bndsChkType != TYP_INT)
         {
-            arrLen = gtNewCastNode(bndsChkType, arrLen, bndsChkType);
+            arrLen = gtNewCastNode(bndsChkType, arrLen, false, bndsChkType);
         }
 
         GenTreeBoundsChk* arrBndsChk = new (this, GT_ARR_BOUNDS_CHECK)
@@ -6198,7 +6198,7 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
         }
         else
         {
-            index = gtNewCastNode(TYP_I_IMPL, index, TYP_I_IMPL);
+            index = gtNewCastNode(TYP_I_IMPL, index, false, TYP_I_IMPL);
         }
     }
 #endif // _TARGET_64BIT_
@@ -6492,7 +6492,7 @@ GenTree* Compiler::fgMorphLocalVar(GenTree* tree, bool forceRemorph)
 
         tree->gtType = TYP_INT;
         fgMorphTreeDone(tree);
-        tree = gtNewCastNode(TYP_INT, tree, varType);
+        tree = gtNewCastNode(TYP_INT, tree, false, varType);
         fgMorphTreeDone(tree);
         return tree;
     }
@@ -11228,13 +11228,15 @@ GenTree* Compiler::fgMorphForRegisterFP(GenTree* tree)
             GenTree* op1 = tree->gtOp.gtOp1;
             GenTree* op2 = tree->gtGetOp2();
 
+            assert(varTypeIsFloating(op1->TypeGet()) && varTypeIsFloating(op2->TypeGet()));
+
             if (op1->TypeGet() != tree->TypeGet())
             {
-                tree->gtOp.gtOp1 = gtNewCastNode(tree->TypeGet(), op1, tree->TypeGet());
+                tree->gtOp.gtOp1 = gtNewCastNode(tree->TypeGet(), op1, false, tree->TypeGet());
             }
             if (op2->TypeGet() != tree->TypeGet())
             {
-                tree->gtOp.gtOp2 = gtNewCastNode(tree->TypeGet(), op2, tree->TypeGet());
+                tree->gtOp.gtOp2 = gtNewCastNode(tree->TypeGet(), op2, false, tree->TypeGet());
             }
         }
     }
@@ -11253,12 +11255,12 @@ GenTree* Compiler::fgMorphForRegisterFP(GenTree* tree)
                 if (op1->TypeGet() == TYP_FLOAT)
                 {
                     assert(op2->TypeGet() == TYP_DOUBLE);
-                    tree->gtOp.gtOp1 = gtNewCastNode(TYP_DOUBLE, op1, TYP_DOUBLE);
+                    tree->gtOp.gtOp1 = gtNewCastNode(TYP_DOUBLE, op1, false, TYP_DOUBLE);
                 }
                 else if (op2->TypeGet() == TYP_FLOAT)
                 {
                     assert(op1->TypeGet() == TYP_DOUBLE);
-                    tree->gtOp.gtOp2 = gtNewCastNode(TYP_DOUBLE, op2, TYP_DOUBLE);
+                    tree->gtOp.gtOp2 = gtNewCastNode(TYP_DOUBLE, op2, false, TYP_DOUBLE);
                 }
             }
         }
@@ -11880,12 +11882,12 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
                     }
                     else
                     {
-                        tree->gtOp.gtOp1 = op1 = gtNewCastNode(TYP_DOUBLE, op1, TYP_DOUBLE);
+                        tree->gtOp.gtOp1 = op1 = gtNewCastNode(TYP_DOUBLE, op1, false, TYP_DOUBLE);
                     }
                 }
                 else if (op2->TypeGet() == TYP_FLOAT)
                 {
-                    tree->gtOp.gtOp2 = op2 = gtNewCastNode(TYP_DOUBLE, op2, TYP_DOUBLE);
+                    tree->gtOp.gtOp2 = op2 = gtNewCastNode(TYP_DOUBLE, op2, false, TYP_DOUBLE);
                 }
                 goto USE_HELPER_FOR_ARITH;
             }
@@ -12091,7 +12093,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
                 fgCastNeeded(op1, info.compRetType))
             {
                 // Small-typed return values are normalized by the callee
-                op1 = gtNewCastNode(TYP_INT, op1, info.compRetType);
+                op1 = gtNewCastNode(TYP_INT, op1, false, info.compRetType);
 
                 // Propagate GTF_COLON_COND
                 op1->gtFlags |= (tree->gtFlags & GTF_COLON_COND);
@@ -13046,7 +13048,7 @@ DONE_MORPHING_CHILDREN:
 
             /* Now we know that we can cast gtOp.gtOp1 of AND to int */
 
-            op1->gtOp.gtOp1 = gtNewCastNode(TYP_INT, op1->gtOp.gtOp1, TYP_INT);
+            op1->gtOp.gtOp1 = gtNewCastNode(TYP_INT, op1->gtOp.gtOp1, false, TYP_INT);
 
             /* now replace the mask node (gtOp.gtOp2 of AND node) */
 
index 69bc99f..ea8f6a5 100644 (file)
@@ -5790,7 +5790,7 @@ bool Compiler::optNarrowTree(GenTree* tree, var_types srct, var_types dstt, Valu
                         if (srcSize == 8)
                         {
                             assert(tree->gtType == TYP_INT);
-                            op1 = gtNewCastNode(TYP_INT, op1, TYP_INT);
+                            op1 = gtNewCastNode(TYP_INT, op1, false, TYP_INT);
 #ifdef DEBUG
                             op1->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED;
 #endif
index 56a1e9d..b49c5f1 100644 (file)
@@ -2513,7 +2513,7 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE                opcode,
                 else
                 {
                     assert(baseType == TYP_UBYTE || baseType == TYP_USHORT);
-                    t1 = gtNewCastNode(TYP_INT, op2, TYP_INT);
+                    t1 = gtNewCastNode(TYP_INT, op2, false, TYP_INT);
                 }
 
                 assert(t1 != nullptr);
index 71dc270..4ccd739 100644 (file)
@@ -1249,7 +1249,7 @@ namespace System.Globalization
             int charsWritten = source.AsSpan().ToUpperInvariant(span);
 
             // Slice the array to the size returned by ToUpperInvariant.
-            int hash = Marvin.ComputeHash32(span.Slice(0, charsWritten).AsBytes(), Marvin.DefaultSeed);
+            int hash = Marvin.ComputeHash32(MemoryMarshal.AsBytes(span.Slice(0, charsWritten)), Marvin.DefaultSeed);
 
             // Return the borrowed array if necessary.
             if (borrowedArr != null)
index 03ba729..d185004 100644 (file)
@@ -1,13 +1,18 @@
 cmake_minimum_required(VERSION 2.8.12.2)
 
+if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
+  # On OSX, we use the libunwind that's part of the OS
+  set(CLR_CMAKE_USE_SYSTEM_LIBUNWIND 1)
+endif(CMAKE_SYSTEM_NAME STREQUAL Darwin)
+
 include_directories(SYSTEM /usr/local/include)
-include_directories(libunwind/include)
 
 add_compile_options(-fPIC)
 
-if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin)
+if(NOT CLR_CMAKE_USE_SYSTEM_LIBUNWIND)
+  include_directories(libunwind/include)
   add_subdirectory(libunwind)
-endif(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin)
+endif(NOT CLR_CMAKE_USE_SYSTEM_LIBUNWIND)
 
 include(configure.cmake)
 
@@ -240,10 +245,9 @@ set(SOURCES
   thread/tls.cpp
 )
 
-if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin)
-  # On OSX, we use the libunwind that's part of the OS
+if(NOT CLR_CMAKE_USE_SYSTEM_LIBUNWIND)
   set(LIBUNWIND_OBJECTS $<TARGET_OBJECTS:libunwind>)
-endif(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin)
+endif(NOT CLR_CMAKE_USE_SYSTEM_LIBUNWIND)
 
 add_library(coreclrpal
   STATIC
@@ -267,10 +271,14 @@ if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
 endif(CMAKE_SYSTEM_NAME STREQUAL Darwin)
 
 if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
+  if(CLR_CMAKE_USE_SYSTEM_LIBUNWIND)
+    find_library(UNWIND unwind)
+  endif()
   find_library(INTL intl)
   target_link_libraries(coreclrpal
     pthread
     rt
+    ${UNWIND}
     ${INTL}
   )
 endif(CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
@@ -303,7 +311,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL Linux)
       ${LZMA})
   endif()
 
-
   if(CLR_MAKE_PLATFORM_ANDROID)
     find_library(ANDROID_SUPPORT NAMES android-support)
     find_library(ANDROID_GLOB NAMES android-glob)
@@ -326,20 +333,56 @@ if(CMAKE_SYSTEM_NAME STREQUAL Linux)
     dl
   )
 
-
   if(NOT INTL STREQUAL INTL-NOTFOUND)
     target_link_libraries(coreclrpal ${INTL})
   endif(NOT INTL STREQUAL INTL-NOTFOUND)
 
+  if(CLR_CMAKE_USE_SYSTEM_LIBUNWIND)
+    if(PAL_CMAKE_PLATFORM_ARCH_ARM)
+      find_library(UNWIND_ARCH NAMES unwind-arm)
+    endif()
+
+    if(PAL_CMAKE_PLATFORM_ARCH_ARM64)
+      find_library(UNWIND_ARCH NAMES unwind-aarch64)
+    endif()
+
+    if(PAL_CMAKE_PLATFORM_ARCH_AMD64)
+      find_library(UNWIND_ARCH NAMES unwind-x86_64)
+    endif()
+
+    if(NOT UNWIND_ARCH STREQUAL UNWIND_ARCH-NOTFOUND)
+      target_link_libraries(coreclrpal ${UNWIND_ARCH})
+    endif()
+
+    find_library(UNWIND_GENERIC NAMES unwind-generic)
+
+    if(NOT UNWIND_GENERIC STREQUAL UNWIND_GENERIC-NOTFOUND)
+      target_link_libraries(coreclrpal ${UNWIND_GENERIC})
+    endif()
+
+    find_library(UNWIND NAMES unwind)
+
+    if(UNWIND STREQUAL UNWIND-NOTFOUND)
+      message(FATAL_ERROR "Cannot find libunwind. Try installing libunwind8-dev or libunwind-devel.")
+    endif()
+
+    target_link_libraries(coreclrpal ${UNWIND})
+
+  endif(CLR_CMAKE_USE_SYSTEM_LIBUNWIND)
+
 endif(CMAKE_SYSTEM_NAME STREQUAL Linux)
 
 if(CMAKE_SYSTEM_NAME STREQUAL NetBSD)
+  if (CLR_CMAKE_USE_SYSTEM_LIBUNWIND)
+    find_library(UNWIND unwind)
+  endif()
   add_definitions(-D_KMEMUSER)
   find_library(INTL intl)
   find_library(KVM kvm)
   target_link_libraries(coreclrpal
     pthread
     rt
+    ${UNWIND}
     ${INTL}
     ${KVM}
   )
index 59a83f4..a1f8b74 100644 (file)
@@ -978,7 +978,9 @@ int main()
 set(SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING 1)
 set(ERROR_FUNC_FOR_GLOB_HAS_FIXED_PARAMS 1)
 
-list(INSERT CMAKE_REQUIRED_INCLUDES 0 ${CMAKE_CURRENT_SOURCE_DIR}/libunwind/include ${CMAKE_CURRENT_BINARY_DIR}/libunwind/include)
+if(NOT CLR_CMAKE_USE_SYSTEM_LIBUNWIND)
+  list(INSERT CMAKE_REQUIRED_INCLUDES 0 ${CMAKE_CURRENT_SOURCE_DIR}/libunwind/include ${CMAKE_CURRENT_BINARY_DIR}/libunwind/include)
+endif()
 
 check_c_source_compiles("
 #include <libunwind.h>
@@ -991,7 +993,9 @@ int main(int argc, char **argv)
         return 0;
 }" UNWIND_CONTEXT_IS_UCONTEXT_T)
 
-list(REMOVE_AT CMAKE_REQUIRED_INCLUDES 0 1)
+if(NOT CLR_CMAKE_USE_SYSTEM_LIBUNWIND)
+  list(REMOVE_AT CMAKE_REQUIRED_INCLUDES 0 1)
+endif()
 
 check_cxx_source_compiles("
 #include <sys/param.h>
index 2b67f19..810c45f 100644 (file)
@@ -43,7 +43,9 @@
 #endif // !FEATURE_PAL
 
 #include "stringarraylist.h"
+#ifdef FEATURE_PERFTRACING
 #include "eventpipe.h"
+#endif // FEATURE_PERFTRACING
 
 #ifdef FEATURE_COMINTEROP
 #include "winrttypenameconverter.h"
@@ -370,7 +372,9 @@ void SetCommandLineArgs(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR* argv)
     CONTRACTL_END;
 
     // Send the command line to EventPipe.
+#ifdef FEATURE_PERFTRACING
     EventPipe::SaveCommandLine(pwzAssemblyPath, argc, argv);
+#endif // FEATURE_PERFTRACING
 
     // Send the command line to System.Environment.
     struct _gc
index ed76fac..fcaa010 100644 (file)
@@ -3851,8 +3851,27 @@ bool UnwindEbpDoubleAlignFrame(
             baseSP = curESP;
             // Set baseSP as initial SP
             baseSP += GetPushedArgSize(info, table, curOffs);
+
             // 16-byte stack alignment padding (allocated in genFuncletProlog)
-            baseSP += 12;
+            // Current funclet frame layout (see CodeGen::genFuncletProlog() and genFuncletEpilog()):
+            //   prolog: sub esp, 12
+            //   epilog: add esp, 12
+            //           ret
+            // SP alignment padding should be added for all instructions except the first one and the last one.
+            TADDR funcletStart = pCodeInfo->GetJitManager()->GetFuncletStartAddress(pCodeInfo);
+
+            const ULONG32 funcletLastInstSize = 1; // 0xc3, ret
+            BOOL atFuncletLastInst = (pCodeInfo->GetRelOffset() + funcletLastInstSize) >= info->methodSize;
+            if (!atFuncletLastInst)
+            {
+                EECodeInfo nextCodeInfo;
+                nextCodeInfo.Init(pCodeInfo->GetCodeAddress() + funcletLastInstSize);
+                atFuncletLastInst = !nextCodeInfo.IsValid() || !nextCodeInfo.IsFunclet() ||
+                    nextCodeInfo.GetJitManager()->GetFuncletStartAddress(&nextCodeInfo) != funcletStart;
+            }
+
+            if (!atFuncletLastInst && funcletStart != pCodeInfo->GetCodeAddress())
+                baseSP += 12;
 
             pContext->PCTAddr = baseSP;
             pContext->ControlPC = *PTR_PCODE(pContext->PCTAddr);
index 64d12e7..d0eea53 100755 (executable)
@@ -100,6 +100,12 @@ countSkippedTests=0
 xunitOutputPath=
 xunitTestOutputPath=
 
+# Variables for text file output. These can be passed back to runtest.sh using the "--playlist" argument
+# to rerun specific tests.
+testsPassOutputPath=
+testsFailOutputPath=
+testsSkipOutputPath=
+
 # libExtension determines extension for dynamic library files
 # runtimeName determines where CoreFX Runtime files will be located
 OSName=$(uname -s)
@@ -301,6 +307,49 @@ function xunit_output_end {
     echo '</assemblies>' >>"$xunitOutputPath"
 }
 
+function text_file_output_begin {
+    if [ -z "$testsPassOutputPath" ]; then
+        testsPassOutputPath=$testRootDir/coreclrtests.pass.txt
+    fi
+    if ! [ -e $(basename "$testsPassOutputPath") ]; then
+        testsPassOutputPath=$testRootDir/coreclrtests.pass.txt
+    fi
+    if [ -e "$testsPassOutputPath" ]; then
+        rm -f "$testsPassOutputPath"
+    fi
+    if [ -z "$testsFailOutputPath" ]; then
+        testsFailOutputPath=$testRootDir/coreclrtests.fail.txt
+    fi
+    if ! [ -e $(basename "$testsFailOutputPath") ]; then
+        testsFailOutputPath=$testRootDir/coreclrtests.fail.txt
+    fi
+    if [ -e "$testsFailOutputPath" ]; then
+        rm -f "$testsFailOutputPath"
+    fi
+    if [ -z "$testsSkipOutputPath" ]; then
+        testsSkipOutputPath=$testRootDir/coreclrtests.skip.txt
+    fi
+    if ! [ -e $(basename "$testsSkipOutputPath") ]; then
+        testsSkipOutputPath=$testRootDir/coreclrtests.skip.txt
+    fi
+    if [ -e "$testsSkipOutputPath" ]; then
+        rm -f "$testsSkipOutputPath"
+    fi
+}
+
+function text_file_output_add_test {
+    local scriptFilePath=$1
+    local testResult=$2 # Pass, Fail, or Skip
+
+    if [ "$testResult" == "Pass" ]; then
+        echo "$scriptFilePath" >>"$testsPassOutputPath"
+    elif [ "$testResult" == "Skip" ]; then
+        echo "$scriptFilePath" >>"$testsSkipOutputPath"
+    else
+        echo "$scriptFilePath" >>"$testsFailOutputPath"
+    fi
+}
+
 function exit_with_error {
     local errorSource=$1
     local errorMessage=$2
@@ -820,11 +869,11 @@ function finish_test {
         header=$header$(printf "[%4ds]" $testRunningTime)
     fi
 
-    local xunitTestResult
+    local testResult
     case $testScriptExitCode in
         0)
             let countPassedTests++
-            xunitTestResult='Pass'
+            testResult='Pass'
             if ((verbose == 1 || runFailingTestsOnly == 1)); then
                 echo "PASSED   - ${header}${scriptFilePath}"
             else
@@ -833,12 +882,12 @@ function finish_test {
             ;;
         2)
             let countSkippedTests++
-            xunitTestResult='Skip'
+            testResult='Skip'
             echo "SKIPPED  - ${header}${scriptFilePath}"
             ;;
         *)
             let countFailedTests++
-            xunitTestResult='Fail'
+            testResult='Fail'
             echo "FAILED   - ${header}${scriptFilePath}"
             ;;
     esac
@@ -850,7 +899,8 @@ function finish_test {
         done <"$outputFilePath"
     fi
 
-    xunit_output_add_test "$scriptFilePath" "$outputFilePath" "$xunitTestResult" "$testScriptExitCode" "$testRunningTime"
+    xunit_output_add_test "$scriptFilePath" "$outputFilePath" "$testResult" "$testScriptExitCode" "$testRunningTime"
+    text_file_output_add_test "$scriptFilePath" "$testResult"
 }
 
 function finish_remaining_tests {
@@ -1258,6 +1308,7 @@ then
 fi
 
 xunit_output_begin
+text_file_output_begin
 create_core_overlay
 precompile_overlay_assemblies
 
diff --git a/tests/runtesttilstable.sh b/tests/runtesttilstable.sh
new file mode 100755 (executable)
index 0000000..43f29e6
--- /dev/null
@@ -0,0 +1,158 @@
+#!/usr/bin/env bash
+
+function print_usage {
+    echo ''
+    echo 'CoreCLR test runner wrapper script.'
+    echo ''
+    echo 'Run tests using runtest.sh, then rerun the failures, if any,'
+    echo 'until the number of failures stabilizes. Thus, when running'
+    echo 'flaky tests, or running tests on a flaky platform, only the'
+    echo 'repeatable, "real", failures are reported.'
+    echo ''
+    echo 'Tests are rerun in sequential mode (passing --sequential to runtest.sh).'
+    echo 'This hopefully avoids resource exhaustion and other parallel run problems.'
+    echo ''
+    echo 'A maximum number of iterations can be specified.'
+    echo ''
+    echo 'Command line:'
+    echo ''
+    echo 'runtesttilstable.sh [options] [arguments for runtest.sh]'
+    echo ''
+    echo 'Any unknown argument is passed directly to runtest.sh.'
+    echo ''
+    echo 'Optional arguments:'
+    echo '  -h|--help                        : Show usage information.'
+    echo '  --max-iterations=<count>         : Specify the maximum number of iterations. Default: 4.'
+    echo ''
+}
+
+function exit_with_error {
+    local errorMessage=$1
+    local printUsage=$2
+
+    if [ -z "$printUsage" ]; then
+        ((printUsage = 0))
+    fi
+
+    echo "$errorMessage"
+    if ((printUsage != 0)); then
+        print_usage
+    fi
+    exit $EXIT_CODE_EXCEPTION
+}
+
+# Handle Ctrl-C. We will stop execution and print the results that
+# we gathered so far.
+function handle_ctrl_c {
+    echo ""
+    echo "*** Stopping... ***"
+    print_results
+    exit_with_error "Test run aborted by Ctrl+C."
+}
+
+# Register the Ctrl-C handler
+trap handle_ctrl_c INT
+
+# Where are we?
+scriptPath=$(dirname $0)
+
+# Exit code constants
+readonly EXIT_CODE_SUCCESS=0       # Script ran normally.
+readonly EXIT_CODE_EXCEPTION=1     # Script exited because something exceptional happened (e.g. bad arguments, Ctrl-C interrupt).
+readonly EXIT_CODE_TEST_FAILURE=2  # Script completed successfully, but one or more tests failed.
+
+# Argument variables
+((maxIterations = 4))
+
+# Handle arguments
+__UnprocessedBuildArgs=
+
+# We need to capture the --testRootDir argument so we know where the test pass/fail/skip files will be placed.
+testRootDir=
+
+# We need to handle the --playlist argument specially. The first run, we pass it through (if passed).
+# After that, we use the --playlist argument ourselves, so we don't pass through the original one.
+playlistArgument=
+
+for i in "$@"
+do
+    case $i in
+        -h|--help)
+            print_usage
+            exit $EXIT_CODE_SUCCESS
+            ;;
+        --max-iterations=*)
+            maxIterations=${i#*=}
+            ;;
+        --playlist=*)
+            playlistArgument=$i
+            ;;
+        --testRootDir=*)
+            testRootDir=${i#*=}
+            # Also pass it on to runtest.sh
+            __UnprocessedBuildArgs="$__UnprocessedBuildArgs $i"
+            ;;
+        *)
+            __UnprocessedBuildArgs="$__UnprocessedBuildArgs $i"
+            ;;
+    esac
+done
+
+# Check testRootDir; this check is also done by runtest.sh.
+
+if [ -z "$testRootDir" ]; then
+    echo "--testRootDir is required."
+    print_usage
+    exit $EXIT_CODE_EXCEPTION
+fi
+if [ ! -d "$testRootDir" ]; then
+    echo "Directory specified by --testRootDir does not exist: $testRootDir"
+    exit $EXIT_CODE_EXCEPTION
+fi
+
+# Now start running the tests.
+
+nextcmd="${scriptPath}/runtest.sh ${playlistArgument} ${__UnprocessedBuildArgs}"
+echo "Running: $nextcmd"
+$nextcmd
+exitCode=$?
+if [ $exitCode -eq $EXIT_CODE_TEST_FAILURE ]; then
+    # Now, we loop, rerunning the failed tests up to maxIterations times minus one
+    # (the initial run counts as an iteration).
+    ((totalRerunCount = $maxIterations - 1))
+    for (( i=1; i<=$totalRerunCount; i++ )); do
+        if [ ! -e "$testRootDir/coreclrtests.fail.txt" ]; then
+            exit_with_error "Error: couldn't find $testRootDir/coreclrtests.fail.txt"
+        fi
+
+        num_errors=$(grep -c '' "$testRootDir/coreclrtests.fail.txt")
+        echo "Test run failed with $num_errors errors:"
+        cat "$testRootDir/coreclrtests.fail.txt"
+        echo ''
+
+        echo "Rerunning failures ($i of $totalRerunCount reruns)..."
+
+        # Move the fail file to a different location, so it can be used without getting trashed by the
+        # next run's error file.
+        retryFile="$testRootDir/coreclrtests.retry.txt"
+        if [ -e "$retryFile" ]; then
+            rm -f "$retryFile"
+            if [ -e "$retryFile" ]; then
+                exit_with_error "Error: couldn't delete $retryFile"
+            fi
+        fi
+        mv "$testRootDir/coreclrtests.fail.txt" "$retryFile"
+
+        nextcmd="${scriptPath}/runtest.sh --sequential --playlist=${retryFile} ${__UnprocessedBuildArgs}"
+        echo "Running: $nextcmd"
+        $nextcmd
+        exitCode=$?
+        if [ $exitCode -ne $EXIT_CODE_TEST_FAILURE ]; then
+            # Either success or exceptional failure; we're done. For test failure, we loop,
+            # if we haven't hit the maximum number of allowed iterations.
+            break
+        fi
+    done
+fi
+
+exit $exitCode
index aecb3e8..3ffff2c 100755 (executable)
@@ -183,21 +183,21 @@ function mount_emulator {
     fi
 
     if [ ! -d "$__ARMEmulRootfs" ]; then
-               sudo mkdir "$__ARMEmulRootfs"
-       fi
+        sudo mkdir "$__ARMEmulRootfs"
+    fi
 
-       if [ ! -f "$__ARMEmulRootfs/arm-emulator-rootfs.tar" ]; then
-           if mountpoint -q -- "$__ARMRootfsMountPath"; then
-               sudo umount -l $__ARMRootfsMountPath
+    if [ ! -f "$__ARMEmulRootfs/arm-emulator-rootfs.tar" ]; then
+        if mountpoint -q -- "$__ARMRootfsMountPath"; then
+            sudo umount -l $__ARMRootfsMountPath
         fi
-               mount_with_checking "" "$__ARMEmulPath/platform/rootfs-t30.ext4" "$__ARMRootfsMountPath"
-               
-               cd $__ARMRootfsMountPath
-               sudo tar -cf "$__ARMEmulRootfs/arm-emulator-rootfs.tar" *
-               cd -
-       fi
+        mount_with_checking "" "$__ARMEmulPath/platform/rootfs-t30.ext4" "$__ARMRootfsMountPath"
+        
+        cd $__ARMRootfsMountPath
+        sudo tar -cf "$__ARMEmulRootfs/arm-emulator-rootfs.tar" *
+        cd -
+    fi
 
-       sudo tar -xf "$__ARMEmulRootfs/arm-emulator-rootfs.tar" -C "$__ARMEmulRootfs"
+    sudo tar -xf "$__ARMEmulRootfs/arm-emulator-rootfs.tar" -C "$__ARMEmulRootfs"
 
     mount_with_checking "-t proc" "/proc"    "$__ARMEmulRootfs/proc"
     mount_with_checking "-o bind" "/dev/"    "$__ARMEmulRootfs/dev"
@@ -209,9 +209,9 @@ function mount_emulator {
     fi
     mount_with_checking "-o bind" "/mnt"     "$__ARMEmulRootfs/bindings/tmp"
 
-       if [ ! -d "$__ARMEmulRootfs/$__TempFolder" ]; then
+    if [ ! -d "$__ARMEmulRootfs/$__TempFolder" ]; then
         sudo mkdir "$__ARMEmulRootfs/$__TempFolder"
-       fi
+    fi
 }
 
 #Cross builds coreclr
@@ -354,7 +354,7 @@ function run_tests {
     sudo chroot $__ARMEmulRootfs /bin/bash -x <<EOF
         cd "$__ARMEmulCoreclr"
         ./tests/runtest.sh --sequential\
-                          --testRootDir=$__testRootDirBase \
+                           --testRootDir=$__testRootDirBase \
                            --mscorlibDir=$__mscorlibDirBase \
                            --coreFxNativeBinDir=$__coreFxNativeBinDirBase \
                            --coreFxBinDir="$__coreFxBinDirBase" \
@@ -509,7 +509,7 @@ if [ "$__ciMode" == "emulator" ]; then
     __skipTests=1
 fi
 
-__coreFxBinDir="./bin/CoreFxBinDir" # TODO-clenup: Just for testing.... 
+__coreFxBinDir="./bin/CoreFxBinDir" # TODO-cleanup: Just for testing.... 
 #Check if the optional arguments are present in the case that testing is to be done
 if [ $__skipTests == 0 ]; then
     exit_if_empty "$__testRootDir" "Testing requested, but --testRootDir not provided" true
diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_13501/GitHub_13501.il b/tests/src/JIT/Regression/JitBlue/GitHub_13501/GitHub_13501.il
new file mode 100644 (file)
index 0000000..7d36305
--- /dev/null
@@ -0,0 +1,97 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+.assembly extern mscorlib { auto }
+.assembly GitHub_13501 { }
+
+.class private Program extends [mscorlib]System.Object
+{
+    .method hidebysig static int32 Main() cil managed 
+    {
+        .entrypoint
+        .maxstack 8
+
+    T1: // Test that add.ovf.un(native int(0), int32(-1)) produces native int(0xFFFFFFFF),
+        // -1 is supposed to be treated as unsigned by add.ovf.un.
+        // Test_Cast_gtFoldExprConst is supposed to result in the compile time evaluation
+        // of the cast in gtFoldExprConst.
+
+        ldc.i4 0
+        conv.i
+        ldc.i4 0
+        call native int Program::Test_Cast_gtFoldExprConst(native int, bool)
+        ldc.i4 -1
+        conv.u
+        bne.un FAIL
+
+    T2: // Same test as above but using Test_Cast_EvalCastForConstantArgs. In this case
+        // -1 is the result of a more complex expression that can be evaluated at compile
+        // time by value numbering in EvalCastForConstantArgs.
+
+        ldc.i4 0
+        conv.i
+        ldc.i4 0
+        call native int Program::Test_Cast_EvalCastForConstantArgs(native int, bool)
+        ldc.i4 -1
+        conv.u
+        bne.un FAIL
+
+    T3: // Same test as above but using Test_Cast_Codegen. In this case -1 is passed
+        // as a parameter so the cast cannot be evaluated at compile time and actual
+        // cast code needs to be generated.
+
+        ldc.i4 0
+        conv.i
+        ldc.i4 -1
+        call native int Program::Test_Cast_Codegen(native int, int32)
+        ldc.i4 -1
+        conv.u
+        bne.un FAIL
+        
+    PASS: 
+        ldstr "PASS"
+        call void [System.Console]System.Console::WriteLine(string)
+        ldc.i4 100
+        ret
+    FAIL:
+        ldstr "FAIL"
+        call void [System.Console]System.Console::WriteLine(string)
+        ldc.i4 1
+        ret
+    }
+  
+    .method static native int Test_Cast_gtFoldExprConst(native int, bool) cil managed noinlining
+    {
+        .maxstack 4
+
+        ldc.i4 -1
+        ldarg.0
+        add.ovf.un
+        ret
+    }
+
+    .method static native int Test_Cast_EvalCastForConstantArgs(native int, bool) cil managed noinlining
+    {
+        .maxstack 4
+
+        ldarg.1
+        brtrue L1
+        ldc.i4 -1
+        br L2
+    L1: ldc.i4 -1
+    L2: ldarg.0
+        add.ovf.un
+        ret
+    }
+
+    .method static native int Test_Cast_Codegen(native int, int32) cil managed noinlining
+    {
+        .maxstack 4
+
+        ldarg.1
+        ldarg.0
+        add.ovf.un
+        ret
+    }
+}
diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_13501/GitHub_13501.ilproj b/tests/src/JIT/Regression/JitBlue/GitHub_13501/GitHub_13501.ilproj
new file mode 100644 (file)
index 0000000..918ae0a
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <CLRTestPriority>1</CLRTestPriority>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "></PropertyGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="GitHub_13501.il" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
\ No newline at end of file
index bcfac7a..81c37ae 100644 (file)
@@ -9,9 +9,12 @@ namespace JitBench
 {
     class MusicStoreBenchmark : WebAppBenchmark
     {
-        public MusicStoreBenchmark() : base("MusicStore") { }
+        public MusicStoreBenchmark() : base("MusicStore", "MusicStore.dll") { }
 
-        protected override string ExecutableName => "MusicStore.dll";
+        protected override string GetJitBenchRepoRootDir(string outputDir)
+        {
+            return Path.Combine(outputDir, "K");
+        }
 
         protected override string GetWebAppSrcDirectory(string outputDir)
         {
@@ -21,9 +24,12 @@ namespace JitBench
 
     class AllReadyBenchmark : WebAppBenchmark
     {
-        public AllReadyBenchmark() : base("AllReady") { }
+        public AllReadyBenchmark() : base("AllReady", "AllReady.dll") { }
 
-        protected override string ExecutableName => "AllReady.dll";
+        protected override string GetJitBenchRepoRootDir(string outputDir)
+        {
+            return Path.Combine(outputDir, "J");
+        }
 
         protected override string GetWebAppSrcDirectory(string outputDir)
         {
@@ -35,13 +41,11 @@ namespace JitBench
     {
         private static readonly HashSet<int> DefaultExitCodes = new HashSet<int>(new[] { 0 });
 
-        public WebAppBenchmark(string name) : base(name)
+        public WebAppBenchmark(string name, string executableName) : base(name)
         {
-            ExePath = ExecutableName;
+            ExePath = executableName;
         }
 
-        protected abstract string ExecutableName { get; }
-
         public override async Task Setup(DotNetInstallation dotNetInstall, string outputDir, bool useExistingSetup, ITestOutputHelper output)
         {
             if(!useExistingSetup)
@@ -53,7 +57,7 @@ namespace JitBench
                     await Publish(dotNetInstall, outputDir, setupSection);
                 }
             }
-            string webAppSrcDirectory = GetWebAppSrcDirectory(outputDir);
+
             string tfm = DotNetSetup.GetTargetFrameworkMonikerForFrameworkVersion(dotNetInstall.FrameworkVersion);
             WorkingDirPath = GetWebAppPublishDirectory(dotNetInstall, outputDir, tfm);
             EnvironmentVariables.Add("DOTNET_SHARED_STORE", GetWebAppStoreDir(outputDir));
@@ -78,7 +82,7 @@ namespace JitBench
                 throw new Exception($"git {arguments} has failed, the exit code was {exitCode}");
         }
 
-        private static async Task CreateStore(DotNetInstallation dotNetInstall, string outputDir, ITestOutputHelper output)
+        private async Task CreateStore(DotNetInstallation dotNetInstall, string outputDir, ITestOutputHelper output)
         {
             string tfm = DotNetSetup.GetTargetFrameworkMonikerForFrameworkVersion(dotNetInstall.FrameworkVersion);
             string rid = $"win7-{dotNetInstall.Architecture}";
@@ -228,10 +232,7 @@ namespace JitBench
             return true;
         }
 
-        protected static string GetJitBenchRepoRootDir(string outputDir)
-        {
-            return Path.Combine(outputDir, "J"); 
-        }
+        protected abstract string GetJitBenchRepoRootDir(string outputDir);
 
         protected abstract string GetWebAppSrcDirectory(string outputDir);
 
@@ -252,23 +253,16 @@ namespace JitBench
             return null;
         }
 
-        static string GetWebAppStoreDir(string outputDir)
+        string GetWebAppStoreDir(string outputDir)
         {
             return Path.Combine(GetJitBenchRepoRootDir(outputDir), StoreDirName);
         }
 
-        string GetLocalJitBenchRepoDirectory()
-        {
-            return Environment.GetEnvironmentVariable("MUSICSTORE_PRIVATE_REPO");
-        }
-
         private const string JitBenchRepoUrl = "https://github.com/aspnet/JitBench";
         private const string JitBenchCommitSha1Id = "6bee730486f272d31f23f1033225090511f856f3";
-        private const string EnvironmentFileName = "JitBenchEnvironment.txt";
         private const string StoreDirName = ".store";
         private readonly Metric StartupMetric = new Metric("Startup", "ms");
         private readonly Metric FirstRequestMetric = new Metric("First Request", "ms");
         private readonly Metric MedianResponseMetric = new Metric("Median Response", "ms");
-        private readonly Metric MeanResponseMetric = new Metric("Mean Response", "ms");
     }
 }
index cd8c819..fe40a4d 100644 (file)
@@ -371,7 +371,7 @@ namespace SoDBench
                     {
                         FileName = s_dotnetExe.FullName,
                         // The UserSharedCompiler flag is set to false to prevent handles from being held that will later cause deletion of the installed SDK to fail.
-                        Arguments = $"publish -c Release --runtime {os} --output {publishDir.FullName} /p:UseSharedCompilation=false",
+                        Arguments = $"publish -c Release --runtime {os} --output {publishDir.FullName} /p:UseSharedCompilation=false /p:UseRazorBuildServer=false",
                         UseShellExecute = false,
                         WorkingDirectory = deploymentSandbox.FullName
                     };
diff --git a/tests/testsFailing.arm.txt b/tests/testsFailing.arm.txt
new file mode 100644 (file)
index 0000000..68ad6a3
--- /dev/null
@@ -0,0 +1 @@
+GC/API/GC/GetAllocatedBytesForCurrentThread/GetAllocatedBytesForCurrentThread.sh