235 | | == Places of interest in the source code == |
236 | | |
237 | | The file [[GhcFile(prelude/primops.txt.pp)]] defines !PrimOps and their type signatures. An example definition looks like this: |
238 | | |
239 | | {{{ |
240 | | primop IntGtOp ">#" Compare Int# -> Int# -> Bool |
241 | | with fixity = infix 4 |
242 | | }}} |
243 | | |
244 | | Existing definitions should remain unchanged or the code using them would break and that is a Very Bad Thing. This would require creating new !PrimOps: |
245 | | |
246 | | {{{ |
247 | | primop IntGtOpB ".>#" Compare Int# -> Int# -> Bool# |
248 | | with fixity = infix 4 |
249 | | }}} |
250 | | |
251 | | The tricky part here is `Compare`. This a value constructor of `PrimOpInfo` data type defined in [[GhcFile(prelude/PrimOp.lhs)]]: |
252 | | |
253 | | {{{ |
254 | | data PrimOpInfo |
255 | | = Dyadic OccName -- string :: T -> T -> T |
256 | | Type |
257 | | | Monadic OccName -- string :: T -> T |
258 | | Type |
259 | | | Compare OccName -- string :: T -> T -> Bool |
260 | | Type |
261 | | | GenPrimOp OccName -- string :: \/a1..an . T1 -> .. -> Tk -> T |
262 | | [TyVar] |
263 | | [Type] |
264 | | Type |
265 | | }}} |
266 | | |
267 | | We would need new `PrimOpInfo` value to denote !PrimOps of type `T -> T -> Bool#`. Appropriate functions like `primOpSig` and `getPrimOpResultInfo` would have to be adjusted accordingly. |
| 235 | == Proposed patch (13/03/2013) == |
| 236 | |
| 237 | The prototype patch posted on the trac implements six new prototype comparison primops: |
| 238 | |
| 239 | {{{ |
| 240 | .># :: Int# -> Int# -> Int# |
| 241 | .<# :: Int# -> Int# -> Int# |
| 242 | .>=# :: Int# -> Int# -> Int# |
| 243 | .<=# :: Int# -> Int# -> Int# |
| 244 | .==# :: Int# -> Int# -> Int# |
| 245 | ./=# :: Int# -> Int# -> Int# |
| 246 | }}} |
| 247 | |
| 248 | Each of these new primops takes two `Int#`s that are to be compared. The result is also an `Int#`: `0#` if the relation between the operands does not hold and `1#` when it does hold. For example `5# .># 3#` returns `1#` and `3# .># 3#` returns `0#`. With the new primops we can rewrite the original expression that motivated the problem: |
| 249 | |
| 250 | {{{ |
| 251 | case (x <# 0#) || (x >=# width) || (y <# 0#) || (y >=# height) of |
| 252 | True -> E1 |
| 253 | False -> E2 |
| 254 | }}} |
| 255 | |
| 256 | as |
| 257 | |
| 258 | {{{ |
| 259 | case (x .<# 0#) `orI#` (x .>=# width) `orI#` (y .<# 0#) `orI#` (y .>=# height) of |
| 260 | True -> E1 |
| 261 | False -> E2 |
| 262 | }}} |
| 263 | |
| 264 | (Note: `orI#` is a bitwise OR operation on operands of type `Int#`. It was introduced together with `andI#`, `notI#` and `xor#` in #7689). Using the LLVM backend this compiles to: |
| 265 | |
| 266 | {{{ |
| 267 | # BB#0: # %c1nK |
| 268 | movq %rsi, %rax |
| 269 | orq %r14, %rax |
| 270 | shrq $63, %rax |
| 271 | cmpq %rdi, %r14 |
| 272 | setge %cl |
| 273 | movzbl %cl, %ecx |
| 274 | orq %rax, %rcx |
| 275 | cmpq %r8, %rsi |
| 276 | setge %al |
| 277 | movzbl %al, %eax |
| 278 | orq %rcx, %rax |
| 279 | jne .LBB2_1 |
| 280 | # BB#3: # %c1ol |
| 281 | movq (%rbp), %rax |
| 282 | movl $r1m6_closure+1, %ebx |
| 283 | jmpq *%rax # TAILCALL |
| 284 | .LBB2_1: # %c1nK |
| 285 | cmpq $1, %rax |
| 286 | jne .LBB2_2 |
| 287 | # BB#4: # %c1ov |
| 288 | movq (%rbp), %rax |
| 289 | movl $r1m7_closure+1, %ebx |
| 290 | jmpq *%rax # TAILCALL |
| 291 | .LBB2_2: # %c1ob |
| 292 | movq r1m5_closure(%rip), %rax |
| 293 | movl $r1m5_closure, %ebx |
| 294 | jmpq *%rax # TAILCALL |
| 295 | }}} |
| 296 | |
| 297 | The assembly does not contain comparisons and jumps in the scrutinee of the case expression, but still does jumps for selecting an appropriate branch of the case expression. |
| 298 | |
| 299 | An alternative design decision is to make the new primops return a `Word#`. I decided to use `Int#` because `Int` is used more often than `Word`. If new primops returned a `Word#` the user would have to use `int2Word#`/`word2Int#` primops to do conversions if she ever wished to box the result. |
| 300 | |
| 301 | Comparisons for `Word#`, `Float#` and `Double#` will be implemented once we make sure that the prototype implementation is correct. |
| 302 | |
| 303 | Some concerns: |
| 304 | - should the primops return an `Int#` or `Word#` as their result? |
| 305 | - what names should the new primops have? I planned to use names with a dot preceeding the operator for `Int#` and `Double#` comparisons (e.g. `./=#`, `.>##`) and names with "St" suffix for `Word#` and `Float#`, e.g. `gtWordSt#`, `gtFloatSt#` (`St` stands for 'strict' because the result can be used with the strict bitwise logical operators). |
| 306 | - how to remove the old `Compare` primops (ones of type `T -> T -> Bool`)? |
| 307 | - once we have the new primops do we really care about the unboxed `Bool#`? |