1 2 .globl _start 3 4 _start: 5 # This code tests for the fldcw "load floating point command word" 6 # instruction. On most x86 processors the retired_instruction 7 # performance counter counts this as one instruction. However, 8 # on Pentium 4 systems it counts as two. Therefore this can 9 # affect BBV results on such a system. 10 # fldcw is most often used to set the rouding mode when doing 11 # floating point to integer conversions 12 13 # It is encoded as "d9 /5" which means 14 # 1101 1001 xx10 1yyy 15 # Where xx is the "mod" which will be 00, 01, or 10 indicating offset 16 # and yyy is the register field 17 18 # these are instructions with similar encodings to fldcw 19 # that can cause false positives if the test isn't explicit enough 20 similar: 21 fld1 # d9 e8 22 fldl2t # d9 e9 23 fldl2e # d9 ea 24 fldpi # d9 eb 25 fldlg2 # d9 ec 26 fldln2 # d9 ed 27 fldz # d9 ee 28 29 # check some varied ways of calling fldcw 30 31 # offset on stack 32 stack: 33 sub $8,%rsp # allocate space on stack 34 fnstcw 2(%rsp) 35 fldcw 2(%rsp) 36 add $8,%rsp # restore stack 37 38 # 64-bit register 39 sixtyfour_reg: 40 fnstcw cw 41 mov $cw,%rax 42 fldcw 0(%rax) # rax 43 mov $cw,%rbx 44 fldcw 0(%rbx) # rbx 45 mov $cw,%rcx 46 fldcw 0(%rcx) # rcx 47 mov $cw,%rdx 48 fldcw 0(%rdx) # rdx 49 50 # 32-bit register 51 52 # Note! The assembler that comes with SuSE 9.1 53 # cannot assemble 32-bit fldcw on 64-bit systems 54 # Hence the need to hand-code them 55 56 57 thirtytwo_reg: 58 fnstcw cw 59 mov $cw,%eax 60 61 # fldcw 0(%eax) # eax 62 .byte 0x67,0xd9,0x28 63 64 mov $cw,%ebx 65 66 # fldcw 0(%ebx) # ebx 67 .byte 0x67,0xd9,0x2b 68 69 mov $cw,%ecx 70 71 # fldcw 0(%ecx) # ecx 72 .byte 0x67,0xd9,0x29 73 74 mov $cw,%edx 75 76 # fldcw 0(%edx) # edx 77 .byte 0x67,0xd9,0x2a 78 79 # register + 8-bit offset 80 eight_bit: 81 mov $cw,%eax 82 sub $32,%eax 83 84 # fldcw 32(%eax) # eax + 8 bit offset 85 .byte 0x67,0xd9,0x68,0x20 86 87 mov %eax,%ebx 88 # fldcw 32(%ebx) # ebx + 8 bit offset 89 .byte 0x67,0xd9,0x6b,0x20 90 91 mov %eax,%ecx 92 93 # fldcw 32(%ecx) # ecx + 8 bit offset 94 .byte 0x67,0xd9,0x69,0x20 95 96 mov %eax,%edx 97 98 # fldcw 32(%edx) # edx + 8 bit offset 99 .byte 0x67,0xd9,0x6a,0x20 100 101 102 # register + 32-bit offset 103 thirtytwo_bit: 104 mov $cw,%eax 105 sub $30000,%eax 106 107 # fldcw 30000(%eax) # eax + 16 bit offset 108 .byte 0x67,0xd9,0xa8,0x30,0x75,0x00,0x00 109 110 mov %eax,%ebx 111 112 # fldcw 30000(%ebx) # ebx + 16 bit offset 113 .byte 0x67,0xd9,0xab,0x30,0x75,0x00,0x00 114 115 mov %eax,%ecx 116 117 # fldcw 30000(%ecx) # ecx + 16 bit offset 118 .byte 0x67,0xd9,0xa9,0x30,0x75,0x00,0x00 119 120 mov %eax,%edx 121 122 # fldcw 30000(%edx) # edx + 16 bit offset 123 .byte 0x67,0xd9,0xaa,0x30,0x75,0x00,0x00 124 125 # check an fp/integer conversion 126 # in a loop to give a bigger count 127 128 mov $1024,%rcx 129 big_loop: 130 131 fldl three # load value onto fp stack 132 fnstcw saved_cw # store control word to mem 133 movzwl saved_cw, %eax # load cw from mem, zero extending 134 movb $12, %ah # set cw for "round to zero" 135 movw %ax, cw # store back to memory 136 fldcw cw # save new rounding mode 137 fistpl result # save stack value as integer to mem 138 fldcw saved_cw # restore old cw 139 140 loop big_loop # loop to make the count more obvious 141 142 movl result, %ebx # sanity check to see if the 143 cmp $3,%rbx # result is the expected one 144 je exit 145 146 print_error: 147 mov $1,%rax # write syscall 148 mov $1,%rdi # stdout 149 mov $error,%rsi # string 150 mov $22,%rdx # length of string 151 syscall 152 153 exit: 154 xor %rdi, %rdi # return 0 155 mov $60, %rax # SYSCALL_EXIT 156 syscall 157 158 159 160 .data 161 saved_cw: .long 0 162 cw: .long 0 163 result: .long 0 164 three: .long 0 # a floating point 3.0 165 .long 1074266112 166 error: .asciz "Error! Wrong result!\n" 167