Skip to content

Commit

Permalink
Unsafe.Unlikely (Assume) draft implementation
Browse files Browse the repository at this point in the history
Just a quick draft for dotnet/runtime#4966 for both RyuJIT CoreCLR and Mono-LLVM (maybe it will help to push it forward since it's 4 years old).

It allows programmers to give hints to JIT about branches' probabilities, e.g. to avoid goto hacks [like this](https://github.com/dotnet/runtime/blob/469ece74b6e5b7e94991dde21b3c2409194ceab9/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs#L523) in perf critical code.

#### Sample:
```csharp
static int Foo(int a)
{
    if (Unsafe.Unlikely(a <= 0))   // hint: `a` is unlikely to be <= 0
        return a * a;

    return 0;
}
```
#### Codegen without `Unlikely`:
```asm
       85D2                 test     edx, edx
       7F06                 jg       SHORT G_M36845_IG05
       8BC2                 mov      eax, edx
       0FAFC2               imul     eax, edx
       C3                   ret  ;; return a * a
G_M36845_IG05:
       8BC2                 mov      eax, edx
       C3                   ret ;; return 0
```
#### Codgen with `Unlikely` (this is what this PR emits)
```asm
       85D2                 test     edx, edx
       7E03                 jle      SHORT G_M4270_IG05
       8BC2                 mov      eax, edx
       C3                   ret  ;; return 0
G_M4270_IG05:
       8BC2                 mov      eax, edx
       0FAFC2               imul     eax, edx
       C3                   ret  ;; return a * a
```

Also, I implemented it for Mono-LLVM (I use `@llvm.expect.i32` intrinsic, which is emitted when you use `__builtin_expect` in C/C++)
#### Mono-LLVM without `Unlikely`
```asm
	mov	ecx, edi
	imul	ecx, ecx   ;; Mono-LLVM emits branchless code for our sample
	xor	eax, eax
	test	edi, edi
	cmovle	eax, ecx
	ret
```
#### Mono-LLVM with `Unlikely`
```asm
	xor	eax, eax
	test	edi, edi
	jle	1 <gram_Foo1__int_+0x7>   ;; branch is better than cmove in this case
	ret                               ;; return 0
	imul	edi, edi
	mov	eax, edi
	ret                               ;; return a * a
```
cc @jkotas
  • Loading branch information
EgorBo committed Mar 2, 2020
1 parent 89902e8 commit 30d2495
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 3 deletions.
12 changes: 10 additions & 2 deletions mono/mini/intrinsics.c
Original file line number Diff line number Diff line change
Expand Up @@ -582,11 +582,19 @@ emit_unsafe_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignatu
MONO_INST_NEW (cfg, ins, OP_NOP);
MONO_ADD_INS (cfg->cbb, ins);
return ins;
}
else if (!strcmp (cmethod->name, "SkipInit")) {
} else if (!strcmp (cmethod->name, "SkipInit")) {
MONO_INST_NEW (cfg, ins, OP_NOP);
MONO_ADD_INS (cfg->cbb, ins);
return ins;
} else if (COMPILE_LLVM (cfg) && !strcmp (cmethod->name, "Unlikely")) {
g_assert (fsig->param_count == 1);
// LLVM should expand it as @llvm.expect.i32
MONO_INST_NEW (cfg, ins, OP_UNLIKELY32);
ins->type = STACK_I4;
ins->dreg = mono_alloc_ireg (cfg);
ins->sreg1 = args [0]->dreg;
MONO_ADD_INS (cfg->cbb, ins);
return ins;
}
#endif

Expand Down
11 changes: 11 additions & 0 deletions mono/mini/mini-llvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ typedef enum {
INTRINS_TRUNCF,
INTRINS_COPYSIGN,
INTRINS_COPYSIGNF,
INTRINS_EXPECT_I32,
INTRINS_EXPECT_I8,
INTRINS_EXPECT_I1,
INTRINS_CTPOP_I32,
Expand Down Expand Up @@ -8057,6 +8058,12 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
#endif /* ENABLE_NETCORE */
#endif /* SIMD */

case OP_UNLIKELY32: {
LLVMValueRef args [] = { lhs, LLVMConstInt (LLVMInt32Type (), 0, FALSE) };
values [ins->dreg] = LLVMBuildCall (builder, get_intrins (ctx, INTRINS_EXPECT_I32), args, 2, "");
break;
}

case OP_DUMMY_USE:
break;

Expand Down Expand Up @@ -9365,6 +9372,7 @@ static IntrinsicDesc intrinsics[] = {
{INTRINS_TRUNCF, "llvm.trunc.f32"},
{INTRINS_COPYSIGN, "llvm.copysign.f64"},
{INTRINS_COPYSIGNF, "llvm.copysign.f32"},
{INTRINS_EXPECT_I32, "llvm.expect.i32"},
{INTRINS_EXPECT_I8, "llvm.expect.i8"},
{INTRINS_EXPECT_I1, "llvm.expect.i1"},
{INTRINS_CTPOP_I32, "llvm.ctpop.i32"},
Expand Down Expand Up @@ -9590,6 +9598,9 @@ add_intrinsic (LLVMModuleRef module, int id)
case INTRINS_POW:
AddFunc2 (module, name, LLVMDoubleType (), LLVMDoubleType (), LLVMDoubleType ());
break;
case INTRINS_EXPECT_I32:
AddFunc2 (module, name, LLVMInt32Type (), LLVMInt32Type (), LLVMInt32Type ());
break;
case INTRINS_EXPECT_I8:
AddFunc2 (module, name, LLVMInt8Type (), LLVMInt8Type (), LLVMInt8Type ());
break;
Expand Down
2 changes: 1 addition & 1 deletion mono/mini/mini-ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -1456,4 +1456,4 @@ MINI_OP(OP_LZCNT64, "lzcnt64", LREG, LREG, NONE)
MINI_OP(OP_POPCNT32, "popcnt32", IREG, IREG, NONE)
MINI_OP(OP_POPCNT64, "popcnt64", LREG, LREG, NONE)


MINI_OP(OP_UNLIKELY32, "unlikely", IREG, IREG, NONE)

0 comments on commit 30d2495

Please sign in to comment.