Search code examples
x86llvminstrumentationllvm-clang

LLVM: changing increment counter to use saturating increment


Clang has a built-in (marked experimental) method for adding simple code coverage counters. Just compile with -fsanitize-coverage=inline-8bit-counters.

Unfortunately the counters can wrap, sometimes unluckily to zero, making them not very trustworthy. I would like to change this to saturating counters, but I'm not very familiar with LLVM.

Currently, if compiling without optimization on x86-64, the instrumentation looks like:

mov    al, [counter]
add    al, 1
mov    [counter], al

or with optimization, it is able to obtain

add    byte ptr [counter], 1

What I'd like is for something like:

add    byte ptr [counter], 1
sbb    byte ptr [counter], 0

Or at least that is how I'd write it. Or maybe a single load and store is faster?

mov    al, [counter]
add    al, 1
sbb    al, 0
mov    [counter], al

Unfortunately I don't really understand LLVM, and am unsure how to translate this to LLVM instrumentation.

Questions:
* It seems like I'd somehow need to tell it to subtract with flags, but how do I refer to the flags?
* Or is there an intrinsic saturating add?
* Or do I have to put in a conditional set to 0xff somehow and hope it is smart enough to optimize without putting in a conditional jump?

I have a feeling I'm not even in the right "mindset" for LLVM, and these questions are off base. So I'd appreciate redirection if my questions themselves are 'wrong'.


For reference, here is what the current LLVM instrumentation looks like.

https://github.com/llvm-mirror/llvm/blob/master/lib/Transforms/Instrumentation/SanitizerCoverage.cpp

if (Options.Inline8bitCounters) {
  auto CounterPtr = IRB.CreateGEP(
      Function8bitCounterArray,
      {ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)});
  auto Load = IRB.CreateLoad(CounterPtr);
  auto Inc = IRB.CreateAdd(Load, ConstantInt::get(Int8Ty, 1));
  auto Store = IRB.CreateStore(Inc, CounterPtr);
  SetNoSanitizeMetadata(Load);
  SetNoSanitizeMetadata(Store);
}

Solution

  • replace the line

      auto Inc = IRB.CreateAdd(Load, ConstantInt::get(Int8Ty, 1));
    

    with

      CallInst *AddOv = IRB.CreateBinaryIntrinsic(Intrinsic::uadd_with_overflow,
                           Load, ConstantInt::get(Int8Ty, 1));
      Value *SumWithOverflowBit = AddOv;
      auto Inc = IRB.CreateSub(
                    IRB.CreateExtractValue(SumWithOverflowBit, 0),  /* sum */
                    IRB.CreateZExt( /* convert from one bit type to 8 bits type */
                       IRB.CreateExtractValue(SumWithOverflowBit, 1) /* overflow */
                          , Int8Ty));
      SetNoSanitizeMetadata(AddOv);
    

    That should do the trick.