Test of V8 Macro Assembler function AssertSmi and AssertNotSmi for x64 platform

Tags: v8, v8-testing


The Assert* group of tests belong to macro assembler tests. Compared to our ppc article, x64 has an edge that it implemented a number of macro assembler tests.

For x64 platforms, these test functions are located in test/cctest/test-macro-assembler-x64.cc This article presents a way to test this function using simple call to AssertNotSmi.

The implementation uses following ideas,

AssertNotSmi does a jump that crashes the execution when an SMI is passed as argument. A better way to say this is that it creates DEBUG Breakpoints following an abort exit. The crash generates a core dump and cctest indicates this as a failure.

Test Implementation

Leveraging this what we do is,

  • Pass non-SMI values if the test does not fail we say the expected functionality is achieved. Expected result for this test is, 100% pass!
  • Create one more function with the purpose that we will pass an SMI value. However, we cannot throw a bunch of values. Because, no matter how many SMI values we throw we get a single fail count for the test.

Naive Approach

The first approach,

TEST(AssertNotSmiNaive) {
  // Allocate an executable page of memory.
  size_t actual_size;
  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
      Assembler::kMinimalBufferSize, &actual_size, true));
  Isolate* isolate = CcTest::i_isolate();
  HandleScope handles(isolate);
  MacroAssembler assembler(isolate, buffer, static_cast<int>(actual_size),

  MacroAssembler* masm = &assembler;

  __ movl(rcx, Immediate(3));
  __ AssertNotSmi(rcx);

  __ xorq(rax, rax);  // Success.
  __ ret(0);

  CodeDesc desc;
  // Call the function from C++.
  int result = FUNCTION_CAST<F0>(buffer)();
  CHECK_EQ(0, result);

This is regular test code for macro-assembler functions around the call to AssertNotSmi,

__ AssertNotSmi(rcx);e

Second Approach

This is how really do it, all code here, gist.github.com/atiq-cs/d3cbcaec1d13968fb464884a7f024607


A typo or temptation to use a cast like (Smi *) is a mistake! Since you want to pass non-SMI value to make the test pass for non-SMI. If you use (Smi *) cast it will always ensure the passed value is an SMI. A non-SMI value will never be passed to AssertNotSmi function.

static void TestAssertNotSmi(MacroAssembler* masm, Label* exit, int id, Smi* value) {
  __ movl(rax, Immediate(id));
  __ Move(rcx, value);
  __ AssertNotSmi(rcx);
  __ j(equal, exit);

However, this is useful for testing the function AssertSmi. Which you will see in later part of the code in mentioned code segment.

The AssertNotSmi Test

We pass a bunch of non-Smi values if the function is implemented properly the test will pass. However, we cannot pass Smi* object since this convert our passed value to Smi. Our auxilary function for the test TestAssertNotSmi jumps to Label exit to indicate error. The condition here is equal since the result of testb would be non-zero (ZF is set and je does a jump).

The AssertSmi Test

It is similar to the test of AssertNotSmi but the condition here is negated since the result of testb would be zero (ZF is not set and jne does a jump).

We also have an additional overload of the function TestAssertSmi so we can pass Smi for testing.

Third Approach

Third approach is an interesting one. However, I am not gonna demonstrate this by code. I will present the idea,

Modify behavior of Check (in src/x64/macro-assembler-x64.cc),

void MacroAssembler::Check(Condition cc, BailoutReason reason) {
  Label L;
  j(cc, &L, Label::kNear);
  // Control will not return here.

or write a custom Check function conditionaly for debug or test framework so that Check does not do a far jump when condition is not satisfied. Rather, we do a controlled jump and based on where it jumped we determine if test for AssertNotSmi failed or succeeded.

No Comments

Add a Comment