Appearance
| 1 | namespace IR.Values is | |
| 2 | use System.Reflection.Metadata.ILOpCode | |
| 3 | ||
| 4 | use Semantic.Types.Type | |
| 5 | ||
| 6 | use Ghul.Pipes | |
| 7 | ||
| 8 | // The whole body of a synthesized `get_hash_code`: each member's | |
| 9 | // hash, folded together. | |
| 10 | // | |
| 11 | // Every operand arrives boxed, so a member of any kind answers - | |
| 12 | // a value type through its box, a bare type parameter through its | |
| 13 | // own hash, and an absent optional through the null arm rather | |
| 14 | // than a call that would throw on it. | |
| 15 | // | |
| 16 | // ldc.i4.0 | |
| 17 | // <operand> // for each, in declaration order | |
| 18 | // dup | |
| 19 | // brfalse <null> | |
| 20 | // callvirt GetHashCode | |
| 21 | // br <combine> | |
| 22 | // <null>: | |
| 23 | // pop | |
| 24 | // ldc.i4.0 | |
| 25 | // <combine>: | |
| 26 | // ... // swap-free: the running total is under | |
| 27 | // add // the member hash, so mul it beforehand | |
| 28 | class HASH_COMBINE: Value is | |
| 29 | operands: Collections.LIST[Value] | |
| 30 | get_hash_code: Semantic.Symbols.Function | |
| 31 | // What the fold starts from: a base's own hash where this type | |
| 32 | // extends one that holds members, so that the members it does | |
| 33 | // not declare are hashed where they are compared. Zero | |
| 34 | // otherwise - `object`'s hash is identity, and two equal | |
| 35 | // values would answer differently through it. | |
| 36 | seed: Value? | |
| 37 | _result_type: Type | |
| 38 | ||
| 39 | type: Type => _result_type | |
| 40 | is_lightweight_pure: bool => false | |
| 41 | ||
| 42 | init( | |
| 43 | operands: Collections.LIST[Value], | |
| 44 | get_hash_code: Semantic.Symbols.Function, | |
| 45 | seed: Value?, | |
| 46 | result_type: Type | |
| 47 | ) is | |
| 48 | super.init() | |
| 49 | ||
| 50 | self.operands = operands | |
| 51 | self.get_hash_code = get_hash_code | |
| 52 | self.seed = seed | |
| 53 | self._result_type = result_type | |
| 54 | si | |
| 55 | ||
| 56 | gen(context: IR.CONTEXT) is | |
| 57 | let body = context.current_srm_body_emitter! | |
| 58 | ||
| 59 | if let s = seed then | |
| 60 | gen(s, context) | |
| 61 | else | |
| 62 | body.ldc_i4(0) | |
| 63 | fi | |
| 64 | ||
| 65 | for operand in operands do | |
| 66 | // Running total first, multiplied before the member's | |
| 67 | // hash is computed, so the two are in the order `add` | |
| 68 | // wants and nothing has to be swapped. | |
| 69 | body.ldc_i4(31) | |
| 70 | body.op(ILOpCode.MUL) | |
| 71 | ||
| 72 | let null_label = IR.LABEL() | |
| 73 | let combine_label = IR.LABEL() | |
| 74 | ||
| 75 | gen(operand, context) | |
| 76 | body.op(ILOpCode.DUP) | |
| 77 | body.branch(ILOpCode.BRFALSE, null_label) | |
| 78 | body.call_virtual(context.resolve_call_target(get_hash_code)) | |
| 79 | body.branch(ILOpCode.BR, combine_label) | |
| 80 | ||
| 81 | body.mark_label(null_label) | |
| 82 | body.op(ILOpCode.POP) | |
| 83 | body.ldc_i4(0) | |
| 84 | ||
| 85 | body.mark_label(combine_label) | |
| 86 | body.op(ILOpCode.ADD) | |
| 87 | od | |
| 88 | si | |
| 89 | ||
| 90 | to_string() -> string => | |
| 91 | "hash-combine:[{type}]({operands |> map(o => "{o}") |> join(",")})" | |
| 92 | si | |
| 93 | si |