Skip to content

Commit

Permalink
chapter 11 extra credit tests, fix undefined behavior in one test, ad…
Browse files Browse the repository at this point in the history
…d invalid_labels to list of test subdirectories
  • Loading branch information
nlsandler committed May 22, 2024
1 parent 1147bd4 commit 6e5f540
Show file tree
Hide file tree
Showing 15 changed files with 252 additions and 45 deletions.
2 changes: 1 addition & 1 deletion expected_results.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions test_framework/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ class TestDirs:
INVALID_SEMANTICS = "invalid_semantics"
INVALID_DECLARATIONS = "invalid_declarations"
INVALID_TYPES = "invalid_types"
INVALID_LABELS = "invalid_labels"
INVALID_STRUCT_TAGS = "invalid_struct_tags"
# valid test programs for parts I & II
# (we'll handle part III test sdifferently)
Expand All @@ -434,6 +435,7 @@ class TestDirs:
TestDirs.INVALID_SEMANTICS,
TestDirs.INVALID_DECLARATIONS,
TestDirs.INVALID_TYPES,
TestDirs.INVALID_LABELS,
TestDirs.INVALID_STRUCT_TAGS,
],
"valid": [TestDirs.VALID],
Expand All @@ -450,6 +452,7 @@ class TestDirs:
TestDirs.INVALID_SEMANTICS,
TestDirs.INVALID_DECLARATIONS,
TestDirs.INVALID_TYPES,
TestDirs.INVALID_LABELS,
TestDirs.INVALID_STRUCT_TAGS,
]
+ dirs["valid"],
Expand All @@ -460,6 +463,7 @@ class TestDirs:
TestDirs.INVALID_SEMANTICS,
TestDirs.INVALID_DECLARATIONS,
TestDirs.INVALID_TYPES,
TestDirs.INVALID_LABELS,
TestDirs.INVALID_STRUCT_TAGS,
]
+ dirs["valid"],
Expand Down
15 changes: 15 additions & 0 deletions test_properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@
"chapter_4/valid/extra_credit/bitwise_and_precedence.c": [
"bitwise"
],
"chapter_4/valid/extra_credit/bitwise_or_precedence.c": [
"bitwise"
],
"chapter_4/valid/extra_credit/bitwise_xor_precedence.c": [
"bitwise"
],
"chapter_5/invalid_parse/extra_credit/compound_initializer.c": [
"compound"
],
Expand Down Expand Up @@ -582,15 +588,24 @@
"chapter_11/invalid_labels/extra_credit/switch_duplicate_cases.c": [
"switch"
],
"chapter_11/invalid_labels/extra_credit/switch_duplicate_cases_2.c": [
"switch"
],
"chapter_11/valid/extra_credit/bitwise_long_op.c": [
"bitwise"
],
"chapter_11/valid/extra_credit/bitshift.c": [
"bitwise"
],
"chapter_11/valid/extra_credit/compound_assign_to_int.c": [
"compound"
],
"chapter_11/valid/extra_credit/compound_assign_to_long.c": [
"compound"
],
"chapter_11/valid/extra_credit/increment_long.c": [
"increment"
],
"chapter_11/valid/extra_credit/switch_int.c": [
"switch"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ int incr_i(void){
if (i == 1) {
i++;
++i;
return 0;
}
return 0;
}

int decr_j(void) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
int switch_statement(int i) {
switch((long) i) {
case 100l: return 0;
/* Even though 100l and 100 have different types, they have the same
* value once converted to the type of the switch expression (long)
* so they conflict
*/
case 100: return 0;
default: return 1;
}
}

int main(void) {
return switch_statement(100);
}
53 changes: 53 additions & 0 deletions tests/chapter_11/valid/extra_credit/bitshift.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Test bit shift operations on long integers; the main focus is making sure
// we type check them correctly
int main(void) {

long l = 137438953472l; // 2^37
int shiftcount = 2;

if (l >> shiftcount != 34359738368l /* 2 ^ 35 */) {
return 1;
}

if (l << shiftcount != 549755813888 /* 2 ^ 39 */) {
return 2;
}

// test w/ immediate right operand too
if (l << 2 != 549755813888 /* 2 ^ 39 */) {
return 3;
}

// use long as right shift operand
// NOTE: we shouldn't perform usual arithmetic conversions here
// (result has same type as left operand) but we won't be able to fully
// validate that until chapter 12
long long_shiftcount = 3l;

// declare some variables near i; we'll make sure they aren't clobbered by
// bit shift operations
int i_neighbor1 = 0;
int i = -2147483645; // -2^31 + 3
int i_neighbor2 = 0;

// should be -2^28
if (i >> long_shiftcount != -268435456) {
return 4;
}

i = -1;
if (i >> 10l != -1) {
return 5;
}

// make sure we didn't shift any bits into i's neighbors
if (i_neighbor1) {
return 6;
}

if (i_neighbor2) {
return 7;
}

return 0;
}
102 changes: 70 additions & 32 deletions tests/chapter_11/valid/extra_credit/bitwise_long_op.c
Original file line number Diff line number Diff line change
@@ -1,34 +1,72 @@
/* Test bitwise &, |, and ^ operations on long integers.
* Make sure we:
* - promote both operands to a common type;
* - actually perform quadword (not longword) operations
* - use appropriate rewrite rules where one operand is an
* immediate that can't fit in a signed 32-bit integer
*/
int main(void) {
/* A long integer where the upper 32 bits are 0 and lower 32 bits are 1 */
long lower_32_bits_set = 4294967295; // 2^32 - 1

/* A long integer where upper 32 bits are 1 and lower 32 bits are 0 */
long upper_32_bits_set = -1 - lower_32_bits_set;

/* Casting a long to an int and back is equivalent to either:
* - setting all upper bits to 1, if bit 32 is 1 (meaning the truncated int is negative)
* or,
* - setting all uppers bits to 0, if bit 32 is 0 (meaning the truncated int is positive)
* Additionally, i & -1 == i for any signed integer i, whether i is a long or an int.
* The loop below validates that these properties holds for a sample of
* roughly 100,000,000 longs.
*/
for (long l = 17179869184; l > 2147483648; l = l - 150) {

int i = (int) l;
if (i >= 0) {
/* use bitwise "and" to zero out upper bits */
if ((l & lower_32_bits_set) != i)
return 1;
} else {
/* use bitwise "or" to set upper bits */
if ((l | upper_32_bits_set) != i)
return 2;
}

/* every bit is set in -1, so l & -1 == l */
if ((l & -1) != l)
return 3;
}
return 0; // success
// basic tests to make sure we're performing quadword operations
long l1 = 71777214294589695l; // 0x00ff00ff00ff00ff
long l2 = -4294967296; // -2^32; upper 32 bits are 1, lower 32 bits are 0

if ((l1 & l2) != 71777214277877760l /* 0x00ff00ff00000000 */) {
return 1;
}

if ((l1 | l2) != -4278255361 /* 0xffffffff00ff00ff */) {
return 2;
}

if ((l1 ^ l2) != -71777218556133121 /* 0x0ff00ff0000ff00ff */) {
return 3;
}

/* Rewrite rules: andq $IMM, m64 doesn't work if $IMM can't fit
* in signed 32-bit int. Ditto for orq and xorq */
if ((-1l & 34359738368l) != 34359738368l) { // 34359738368 == 2^35
return 4;
}

if ((0l | 34359738368l) != 34359738368l) {
return 5;
}

// 137438953472 == 2^37;
if ((34359738368l ^ 137438953472l) != 171798691840l) {
return 6;
}

/* Typechecking: promote both operands to common type */
long l = 4611686018427387903l; // 0x3fffffffffffffff
// if we try to use i in longword bitwise op without sign-extending it
// first, we may try to read neighboring values l and i2
int i = -1073741824; // 0b1100....0, or 0xc0000000
int i2 = -1;

// 1. sign-extend i to 64 bits; upper 32 bits are all 1s
// 2. take bitwise AND of sign-extended value with l
// 3. result is 3fffffffc0000000; upper bits match l, lower bits match i
if ((i & l) != 4611686017353646080l) {
return 7;
}

// i is sign-extended so upper bytes are 1s; lower bytes of l are 1s
if ((l | i) != -1) {
return 8;
}

// 0x3fffffffffffffff ^ 0xffffffffc0000000 = 0xc00000003fffffff
if ((l ^ i) != -4611686017353646081) {
return 9;
}

// 1. sign extend i2; value is still -1
// 2. XOR result w/ 0x3fffffffffffffff (as a constant this time)
// 3. result is the same as taking bitwise complement of 0x3fffffffffffffff
if ((i2 ^ 4611686018427387903l) != ~4611686018427387903l) {
return 10;
}

return 0; // success
}
38 changes: 37 additions & 1 deletion tests/chapter_11/valid/extra_credit/compound_assign_to_int.c
Original file line number Diff line number Diff line change
@@ -1,11 +1,47 @@
int main(void) {
int i = -20;
int b = 2147483647;
int c = -5000000;

/* This statement is evaluated as follows:
* 1. sign-extend i to a long with value -20
* 2. add this long to 2147483648, resulting in the long 2147483628,
* 3. convert this to an int with value 2147483628 (this value
* can be represented as an int)
*/
i += 2147483648l;
return (i == 2147483628);

// make sure we got the right answer and didn't clobber b
if (i != 2147483628) {
return 1;
}
if (b != 2147483647) {
return 2;
}

// b /= -2^35 + 1
// if we try to perform int (rather than long)
// division, we'll interpret this value as 1 and
// b's value won't change.
b /= -34359738367l;
if (b) { // b's value should be 0
return 3;
}

// make sure we didn't clobber i or c
if (i != 2147483628) {
return 4;
}
if (c != -5000000) {
return 5;
}

// this result will be outside the range of int; we'll
// convert it to int in the usual implementation-defined way
c *= 10000l;
if (c != 1539607552) {
return 6;
}

return 0;
}
11 changes: 7 additions & 4 deletions tests/chapter_11/valid/extra_credit/compound_assign_to_long.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
int main(void) {
long l = -1;
long l = -34359738368l; // -2^35
int i = -10;
/* We should convert i to a long, then add it to l */
l += i;
return (l == -11);
/* We should convert i to a long, then subtract from l */
l -= i;
if (l != -34359738358l) {
return 1;
}
return 0;
}
22 changes: 22 additions & 0 deletions tests/chapter_11/valid/extra_credit/increment_long.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// make sure we support prefix and postfix ++/-- on long variables
int main(void) {
long x = -9223372036854775807l;

// postfix ++
if (x++ != -9223372036854775807l) {
return 1;
}
if (x != -9223372036854775806l) {
return 2;
}

// prefix --
if (--x != -9223372036854775807l) {
return 3;
}
if (x != -9223372036854775807l) {
return 4;
}

return 0; // success
}
10 changes: 5 additions & 5 deletions tests/chapter_11/valid/extra_credit/switch_int.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,16 @@ int main(void) {
* that we get the expected result
*/
if (switch_on_int(5) != 0)
return 0;
return 1;
if (switch_on_int(0) != 1)
return 0;
return 2;
if (switch_on_int(-1) != 2)
return 0;
return 3;
/* 17179869184 is 2^34; it will be truncated to 0
* when passed as a parameter to switch_on_int
*/
if (switch_on_int(17179869184) != 1)
return 0;
return 4;

return 1;
return 0;
}
6 changes: 5 additions & 1 deletion tests/chapter_3/valid/extra_credit/bitwise_shiftr_negative.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
/* Make sure we use arithmetic rather than logical right shift */
/* Make sure we use arithmetic rather than logical right shift.
* NOTE: right bitshift of negative value is implementation-defined;
* we follow GCC and use sign extension
* (see https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html)
* */
int main(void) {
return -5 >> 30;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
#endif

int main(void) {
// & has lower precedence than ==
return 5 & 7 == 5;
}
8 changes: 8 additions & 0 deletions tests/chapter_4/valid/extra_credit/bitwise_or_precedence.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifdef SUPPRESS_WARNINGS
#pragma GCC diagnostic ignored "-Wparentheses"
#endif

int main(void) {
// | has lower precedence than !=
return 5 | 7 != 5;
}
Loading

0 comments on commit 6e5f540

Please sign in to comment.