From d8f744dbd3ee9f3189a46e1886b63dc1a9fd5efc Mon Sep 17 00:00:00 2001 From: Anton Lapounov Date: Thu, 12 Dec 2019 15:21:58 -0800 Subject: [PATCH] Support targeting ARM64 in crossgen2 (#775) With these changes compilation for ARM64 finishes successfully and generates a R2R image. There are some execution issues that need addressing. --- .../Compiler/DependencyAnalysis/Relocation.cs | 99 ++++++++++++++++++++++ .../Target_ARM64/ARM64Emitter.cs | 59 +++++++------ .../src/tools/Common/JitInterface/CorInfoImpl.cs | 34 ++++++-- .../ReadyToRun/Target_ARM64/ImportThunk.cs | 52 +++++++++++- .../ReadyToRunMetadataFieldLayoutAlgorithm.cs | 9 ++ .../ObjectWriter/RelocationHelper.cs | 20 ++++- .../ObjectWriter/TargetExtensions.cs | 3 + .../src/tools/crossgen2/crossgen2/Program.cs | 1 + 8 files changed, 241 insertions(+), 36 deletions(-) diff --git a/src/coreclr/src/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/src/tools/Common/Compiler/DependencyAnalysis/Relocation.cs index f102131..3bd2c65 100644 --- a/src/coreclr/src/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/src/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -167,6 +167,95 @@ namespace ILCompiler.DependencyAnalysis Debug.Assert((uint)GetThumb2BlRel24(p) == imm24); } + //***************************************************************************** + // Extract the PC-Relative offset from an adrp instruction + //***************************************************************************** + private static unsafe int GetArm64Rel21(uint* pCode) + { + int adrpInstr = (int)*pCode; + + // 23-5 bits for the high part. Shift it by 5. + int immhi = (adrpInstr & 0xFFFFE0) >> 5; + // 30,29 bits for the lower part. Shift it by 29. + int immlo = (adrpInstr & 0x60000000) >> 29; + + // Merge them + int imm21 = (immhi << 2) | immlo; + + return imm21; + } + + //***************************************************************************** + // Returns whether the offset fits into an Arm64 adrp instruction + //***************************************************************************** + private static bool FitsInRel21(int val32) + { + return (val32 >= 0) && (val32 <= 0x001FFFFF); + } + + //***************************************************************************** + // Deposit the PC-Relative offset 'imm21' into an adrp instruction + //***************************************************************************** + private static unsafe void PutArm64Rel21(uint* pCode, int imm21) + { + // Verify that we got a valid offset + Debug.Assert(FitsInRel21(imm21)); + + uint adrpInstr = *pCode; + // Check adrp opcode 1ii1 0000 ... + Debug.Assert((adrpInstr & 0x9F000000) == 0x90000000); + + adrpInstr &= 0x9F00001F; // keep bits 31, 28-24, 4-0. + int immlo = imm21 & 0x03; // Extract low 2 bits which will occupy 30-29 bits. + int immhi = (imm21 & 0x1FFFFC) >> 2; // Extract high 19 bits which will occupy 23-5 bits. + adrpInstr |= (uint)((immlo << 29) | (immhi << 5)); + + *pCode = adrpInstr; // write the assembled instruction + + Debug.Assert(GetArm64Rel21(pCode) == imm21); + } + + //***************************************************************************** + // Extract the PC-Relative offset from an add instruction + //***************************************************************************** + private static unsafe int GetArm64Rel12(uint* pCode) + { + uint addInstr = *pCode; + + // 21-10 contains value. Mask 12 bits and shift by 10 bits. + int imm12 = (int)(addInstr & 0x003FFC00) >> 10; + + return imm12; + } + + //***************************************************************************** + // Returns whether the offset fits into an Arm64 add instruction + //***************************************************************************** + private static bool FitsInRel12(int val32) + { + return (val32 >= 0) && (val32 <= 0x00000FFF); + } + + //***************************************************************************** + // Deposit the PC-Relative offset 'imm12' into an add instruction + //***************************************************************************** + private static unsafe void PutArm64Rel12(uint* pCode, int imm12) + { + // Verify that we got a valid offset + Debug.Assert(FitsInRel12(imm12)); + + uint addInstr = *pCode; + // Check add opcode 1001 0001 00... + Debug.Assert((addInstr & 0xFFC00000) == 0x91000000); + + addInstr &= 0xFFC003FF; // keep bits 31-22, 9-0 + addInstr |= (uint)(imm12 << 10); // Occupy 21-10. + + *pCode = addInstr; // write the assembled instruction + + Debug.Assert(GetArm64Rel12(pCode) == imm12); + } + public Relocation(RelocType relocType, int offset, ISymbolNode target) { RelocType = relocType; @@ -194,6 +283,12 @@ namespace ILCompiler.DependencyAnalysis case RelocType.IMAGE_REL_BASED_THUMB_BRANCH24: PutThumb2BlRel24((ushort*)location, (uint)value); break; + case RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21: + PutArm64Rel21((uint*)location, (int)value); + break; + case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A: + PutArm64Rel12((uint*)location, (int)value); + break; default: Debug.Fail("Invalid RelocType: " + relocType); break; @@ -218,6 +313,10 @@ namespace ILCompiler.DependencyAnalysis return (long)GetThumb2Mov32((ushort*)location); case RelocType.IMAGE_REL_BASED_THUMB_BRANCH24: return (long)GetThumb2BlRel24((ushort*)location); + case RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21: + return GetArm64Rel21((uint*)location); + case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A: + return GetArm64Rel12((uint*)location); default: Debug.Fail("Invalid RelocType: " + relocType); return 0; diff --git a/src/coreclr/src/tools/Common/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs b/src/coreclr/src/tools/Common/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs index da3d8b6..f2add85 100644 --- a/src/coreclr/src/tools/Common/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs +++ b/src/coreclr/src/tools/Common/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs @@ -29,9 +29,30 @@ namespace ILCompiler.DependencyAnalysis.ARM64 throw new NotImplementedException(); } - public void EmitMOV(Register regDst, int imm32) + public void EmitMOV(Register regDst, ushort imm16) { - throw new NotImplementedException(); + Debug.Assert((uint)regDst <= 0x1f); + uint instruction = 0xd2800009u | ((uint)imm16 << 5) | (uint)regDst; + Builder.EmitUInt(instruction); + } + + // ldr regDst, [PC + imm19] + public void EmitLDR(Register regDst, short offset) + { + Debug.Assert((uint)regDst <= 0x1f); + Debug.Assert((offset & 3) == 0); + // Sign-extend offset and take 19 bits + uint instruction = 0x58000000 | ((uint)((int)offset & 0x1ffffc) << 3) | (uint)regDst; + Builder.EmitUInt(instruction); + } + + // ldr regDst, [regAddr] + public void EmitLDR(Register regDst, Register regAddr) + { + Debug.Assert((uint)regDst <= 0x1f); + Debug.Assert((uint)regAddr <= 0x1f); + uint instruction = 0xf9400000 | ((uint)regAddr << 5) | (uint)regDst; + Builder.EmitUInt(instruction); } public void EmitLEAQ(Register reg, ISymbolNode symbol, int delta = 0) @@ -59,30 +80,16 @@ namespace ILCompiler.DependencyAnalysis.ARM64 { if (symbol.RepresentsIndirectionCell) { - // xip0 register num is 0x10 - - // ADRP xip0, [symbol (21bit ADRP thing)] - // 0x90000000 + (xip regnum) - Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21); - Builder.EmitByte(0x10); - Builder.EmitByte(0x00); - Builder.EmitByte(0x00); - Builder.EmitByte(0x90); - - // LDR xip0, [xip0 + 12bit LDR page offset reloc)] - // 0xF9400000 + ((xip0 regnum) << 5) + (xip regnum) - Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L); - Builder.EmitByte(0x10); - Builder.EmitByte(0x02); - Builder.EmitByte(0x40); - Builder.EmitByte(0xF9); - - // BR xip0 - // 0xD61F0000 + (xip0 regnum) << 5) - Builder.EmitByte(0x00); - Builder.EmitByte(0x02); - Builder.EmitByte(0x1F); - Builder.EmitByte(0xD6); + // ldr x12, [PC+0xc] + EmitLDR(Register.X12, 0xc); + + // ldr x12, [x12] + EmitLDR(Register.X12, Register.X12); + + // br x12 + Builder.EmitUInt(0xd61f0180); + + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_DIR64); } else { diff --git a/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs index 65d804d..0e0d950 100644 --- a/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs @@ -41,6 +41,7 @@ namespace Internal.JitInterface IA64 = 0x0200, AMD64 = 0x8664, ARM = 0x01c4, + ARM64 = 0xaa64, } private const string JitLibrary = "clrjitilc"; @@ -2887,19 +2888,41 @@ namespace Internal.JitInterface partial void findKnownBBCountBlock(ref BlockType blockType, void* location, ref int offset); + // Translates relocation type constants used by JIT (defined in winnt.h) to RelocType enumeration + private static RelocType GetRelocType(TargetArchitecture targetArchitecture, ushort fRelocType) + { + if (targetArchitecture != TargetArchitecture.ARM64) + return (RelocType)fRelocType; + + const ushort IMAGE_REL_ARM64_PAGEBASE_REL21 = 4; + const ushort IMAGE_REL_ARM64_PAGEOFFSET_12A = 6; + + switch (fRelocType) + { + case IMAGE_REL_ARM64_PAGEBASE_REL21: + return RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21; + case IMAGE_REL_ARM64_PAGEOFFSET_12A: + return RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A; + default: + Debug.Fail("Invalid RelocType: " + fRelocType); + return 0; + }; + } + private void recordRelocation(void* location, void* target, ushort fRelocType, ushort slotNum, int addlDelta) { - // slotNum is not unused + // slotNum is not used Debug.Assert(slotNum == 0); int relocOffset; BlockType locationBlock = findKnownBlock(location, out relocOffset); Debug.Assert(locationBlock != BlockType.Unknown, "BlockType.Unknown not expected"); + TargetArchitecture targetArchitecture = _compilation.TypeSystemContext.Target.Architecture; + if (locationBlock != BlockType.Code) { // TODO: https://github.com/dotnet/corert/issues/3877 - TargetArchitecture targetArchitecture = _compilation.TypeSystemContext.Target.Architecture; if (targetArchitecture == TargetArchitecture.ARM) return; throw new NotImplementedException("Arbitrary relocs"); @@ -2938,12 +2961,13 @@ namespace Internal.JitInterface relocDelta += addlDelta; + RelocType relocType = GetRelocType(targetArchitecture, fRelocType); // relocDelta is stored as the value - Relocation.WriteValue((RelocType)fRelocType, location, relocDelta); + Relocation.WriteValue(relocType, location, relocDelta); if (_relocs.Count == 0) _relocs.EnsureCapacity(_code.Length / 32 + 1); - _relocs.Add(new Relocation((RelocType)fRelocType, relocOffset, relocTarget)); + _relocs.Add(new Relocation(relocType, relocOffset, relocTarget)); } private ushort getRelocTypeHint(void* target) @@ -2977,7 +3001,7 @@ namespace Internal.JitInterface case TargetArchitecture.ARM: return (uint)ImageFileMachine.ARM; case TargetArchitecture.ARM64: - return (uint)ImageFileMachine.ARM; + return (uint)ImageFileMachine.ARM64; default: throw new NotImplementedException("Expected target architecture is not supported"); } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs index c1982a4..354b060 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using System; -using Internal.Text; -using Internal.TypeSystem; + +using ILCompiler.DependencyAnalysis.ARM64; namespace ILCompiler.DependencyAnalysis.ReadyToRun { @@ -14,9 +14,53 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun /// public partial class ImportThunk { - protected override void EmitCode(NodeFactory factory, ref ARM64.ARM64Emitter instructionEncoder, bool relocsOnly) + protected override void EmitCode(NodeFactory factory, ref ARM64Emitter instructionEncoder, bool relocsOnly) { - throw new NotImplementedException(); + + switch (_thunkKind) + { + case Kind.Eager: + break; + + case Kind.DelayLoadHelper: + case Kind.VirtualStubDispatch: + // x11 contains indirection cell + // Do nothing x11 contains our first param + + if (!relocsOnly) + { + // movz x9, #index + int index = _instanceCell.Table.IndexFromBeginningOfArray; + instructionEncoder.EmitMOV(Register.X9, checked((ushort)index)); + } + + // Move Module* -> x10 + // ldr x10, [PC+0x1c] + instructionEncoder.EmitLDR(Register.X10, 0x1c); + + // ldr x10, [x10] + instructionEncoder.EmitLDR(Register.X10, Register.X10); + break; + + case Kind.Lazy: + // Move Module* -> x1 + // ldr x1, [PC+0x1c] + instructionEncoder.EmitLDR(Register.X1, 0x1c); + + // ldr x1, [x1] + instructionEncoder.EmitLDR(Register.X1, Register.X1); + break; + + default: + throw new NotImplementedException(); + } + + // branch to helper + instructionEncoder.EmitJMP(_helperCell); + + // Emit relocation for the Module* load above + if (_thunkKind != Kind.Eager) + instructionEncoder.Builder.EmitReloc(_moduleImport, RelocType.IMAGE_REL_BASED_DIR64); } } } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs index f500755..879d69e 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs @@ -99,6 +99,11 @@ namespace ILCompiler /// private const int DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobAmd64 = 8; + /// + /// CoreCLR DomainLocalModule::NormalDynamicEntry::OffsetOfDataBlob for Arm64 + /// + private const int DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobArm64 = 8; + protected override bool CompareKeyToValue(EcmaModule key, ModuleFieldLayout value) { return key == value.Module; @@ -389,6 +394,10 @@ namespace ILCompiler nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobAmd64; break; + case TargetArchitecture.ARM64: + nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobArm64; + break; + default: throw new NotImplementedException(); } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs index 80072e7..5013d0d 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs @@ -189,7 +189,21 @@ namespace ILCompiler.PEWriter delta = targetRVA - sourceRVA - 4; break; } - + + case RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21: + { + relocationLength = 4; + delta = (targetRVA - sourceRVA) >> 12; + break; + } + + case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A: + { + relocationLength = 4; + delta = targetRVA & 0xfff; + break; + } + default: throw new NotSupportedException(); } @@ -202,6 +216,10 @@ namespace ILCompiler.PEWriter fixed (byte *bufferContent = _relocationBuffer) { long value = Relocation.ReadValue(relocationType, bufferContent); + // Supporting non-zero values for ARM64 would require refactoring this function + if (((relocationType == RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21) || (relocationType == RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A)) && (value != 0)) + throw new NotSupportedException(); + Relocation.WriteValue(relocationType, bufferContent, unchecked(value + delta)); } } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs index 39bf479..201d6f7 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs @@ -39,6 +39,9 @@ namespace ILCompiler.PEWriter case Internal.TypeSystem.TargetArchitecture.X86: return Machine.I386; + case Internal.TypeSystem.TargetArchitecture.ARM64: + return Machine.Arm64; + default: throw new NotImplementedException(target.Architecture.ToString()); } diff --git a/src/coreclr/src/tools/crossgen2/crossgen2/Program.cs b/src/coreclr/src/tools/crossgen2/crossgen2/Program.cs index cf6c622..ee017ca 100644 --- a/src/coreclr/src/tools/crossgen2/crossgen2/Program.cs +++ b/src/coreclr/src/tools/crossgen2/crossgen2/Program.cs @@ -407,6 +407,7 @@ namespace ILCompiler for (int i = 0; i < failingMethod.Instantiation.Length; i++) Console.Write($" --singlemethodgenericarg \"{formatter.FormatName(failingMethod.Instantiation[i], true)}\""); + Console.WriteLine(); return false; } -- 2.7.4