I have almost zero experience with ARM, but off the top of my head there are several differences between the two which may affect performance:
ARM has three operand instructions - the result can be written to a third register which is neither of the source registers
X86 has two operand instructions - the result typically overwrites one of the inputs
X86 can encode memory load and arithmetic in one instruction, ARM needs two instructions for that
X86 has variable length instructions and some common ones are very short
ARM64 doesn't have variable length instructions and is easier to decode
A lot of these things cancel out. Two instructions vs two uops on a complex instruction makes very little difference. Complex addressing modes might as well be instructions as they need their own "opcode-like" instruction bytes. The only thing complex addressing modes gains you is not having to specifically find and name a temporary register for the thing you loaded from memory -- which admittedly can be important if you only have 8 programmer-visible registers, but doesn't matter at all when you have 32.
Let me just concentrate on x86's variable length instructions, which are not at all designed optimally for code size. Bear in mind there are a total of 256 possible 1-byte opcodes.
- "X86 has variable length instructions and some common ones are very short"
- yes, some of them. But x86_64 got rid of the very handy and commonly-used 1 byte 0x40-0x4f INC/DEC A/C/D/B/SP/BP/SI/DI and makes you use 2 byte add/sub instructions instead
- there are a ton of 1 byte instructions which are 1 byte because, apparently, they only need one opcode, and not because they are common. For example 6 Add-with-carry and 6 subtract-with-borrow instructions. Sure, you need them (kinda) sometimes, but they are very very rare, especially in 32 bit or 64 bit code. AAA, DAA, DAS, XLAT, AAM, AAD, IN, OUT, HLT, LES, LDS (at least LSS, LFS, LGS were hidden away in longer opcodes), CMC, CLC, CLD, CLI, STC, STD, STI.
- many common, or at least not unusual or weird, things are quite long. For example (omitting the return instruction in each case)...
int64_t foo(int64_t n){return n + 128;}
x86_64: 48 8d 87 80 00 00 00 lea 0x80(%rdi),%rax
Aarch64: 91020000 add x0, x0, #0x80
RV64: 08050513 addi a0,a0,128
PowerPC64: 38 63 00 80 addi r3,r3,128
Everything is 4 bytes except x86_64 which is 7 bytes. And it doesn't even use an ADD instruction, but uses an addressing mode instead! If it used an add instruction it would be:
48 89 f8 mov %rdi,%rax
48 05 80 00 00 00 add $0x80,%rax
The actual add is a little shorter at 6 bytes, but in total 9 bytes are needed.
As a bonus, 32 bit ARM (Thumb2):
Thumb2:
3080 adds r0, #128
f141 0100 adc.w r1, r1, #0
Two instructions and 6 bytes, but it's still shorter than x86_64. If it was a 32 bit value then only the first, 2 byte, instruction would be needed.
I don't know how real world code density (bytes per actual work done) compares between these architectures, and this too can affect performance because of more or less "work" fitting in instruction cache.
Aarch64 and x86_64 tend to overall work out to very very similar overall code size and overall uops executed.
In 64-bit land, RISC-V 64 is the code size winner by quite a long way -- and the number of uops is again very very similar.
Here's a four year old presentation on this subject by Chris Celio who did the OOO RISC-V "BOOM" CPU core and now works designing x86 CPUs at Intel:
The ultimate proof is in the pudding. Which laptop will be the fastest to compile a million lines of C++ code and to do so without catching on fire? 
Definitely.
My AS Mac Mini has cleared customs in Auckland and has another 300 km to come. I'll have it in 2-3 days maximum and let everyone know my results building binutils, gcc, llvm firefox and other things like that.