PPenguin
PPenguin

Reputation: 329

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);
}

Upvotes: 2

Views: 375

Answers (2)

hexcoder
hexcoder

Reputation: 1220

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.

Upvotes: 3

arnt
arnt

Reputation: 9675

LLVM IR doesn't have flags, or even variables. What you want is probably do add a comparison and a select before the store. Compare Load against 255, then select either Load or Inc depending on the comparison, finally store the result of the select in the CreateStore.

Upvotes: 2

Related Questions