OLD | NEW |
---|---|
1 // Copyright 2006-2009 the V8 project authors. All rights reserved. | 1 // Copyright 2006-2009 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 736 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
747 // be either smi or heap number objects (fp values). Requirements: | 747 // be either smi or heap number objects (fp values). Requirements: |
748 // operand_1 on TOS+1 , operand_2 on TOS+2; Returns operands as | 748 // operand_1 on TOS+1 , operand_2 on TOS+2; Returns operands as |
749 // floating point numbers on FPU stack. | 749 // floating point numbers on FPU stack. |
750 static void LoadFloatOperands(MacroAssembler* masm, Register scratch); | 750 static void LoadFloatOperands(MacroAssembler* masm, Register scratch); |
751 // Test if operands are smi or number objects (fp). Requirements: | 751 // Test if operands are smi or number objects (fp). Requirements: |
752 // operand_1 in eax, operand_2 in edx; falls through on float | 752 // operand_1 in eax, operand_2 in edx; falls through on float |
753 // operands, jumps to the non_float label otherwise. | 753 // operands, jumps to the non_float label otherwise. |
754 static void CheckFloatOperands(MacroAssembler* masm, | 754 static void CheckFloatOperands(MacroAssembler* masm, |
755 Label* non_float, | 755 Label* non_float, |
756 Register scratch); | 756 Register scratch); |
757 // Takes the operands in edx and eax and loads them as integers in eax | |
758 // and ecx. | |
759 static void LoadAsIntegers(MacroAssembler* masm, | |
760 Label* operand_conversion_failure); | |
757 // Test if operands are numbers (smi or HeapNumber objects), and load | 761 // Test if operands are numbers (smi or HeapNumber objects), and load |
758 // them into xmm0 and xmm1 if they are. Jump to label not_numbers if | 762 // them into xmm0 and xmm1 if they are. Jump to label not_numbers if |
759 // either operand is not a number. Operands are in edx and eax. | 763 // either operand is not a number. Operands are in edx and eax. |
760 // Leaves operands unchanged. | 764 // Leaves operands unchanged. |
761 static void LoadSse2Operands(MacroAssembler* masm, Label* not_numbers); | 765 static void LoadSse2Operands(MacroAssembler* masm, Label* not_numbers); |
762 }; | 766 }; |
763 | 767 |
764 | 768 |
765 const char* GenericBinaryOpStub::GetName() { | 769 const char* GenericBinaryOpStub::GetName() { |
766 if (name_ != NULL) return name_; | 770 if (name_ != NULL) return name_; |
767 const int len = 100; | 771 const int kMaxNameLength = 100; |
768 name_ = Bootstrapper::AllocateAutoDeletedArray(len); | 772 name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength); |
769 if (name_ == NULL) return "OOM"; | 773 if (name_ == NULL) return "OOM"; |
770 const char* op_name = Token::Name(op_); | 774 const char* op_name = Token::Name(op_); |
771 const char* overwrite_name; | 775 const char* overwrite_name; |
772 switch (mode_) { | 776 switch (mode_) { |
773 case NO_OVERWRITE: overwrite_name = "Alloc"; break; | 777 case NO_OVERWRITE: overwrite_name = "Alloc"; break; |
774 case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break; | 778 case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break; |
775 case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break; | 779 case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break; |
776 default: overwrite_name = "UnknownOverwrite"; break; | 780 default: overwrite_name = "UnknownOverwrite"; break; |
777 } | 781 } |
778 | 782 |
779 OS::SNPrintF(Vector<char>(name_, len), | 783 OS::SNPrintF(Vector<char>(name_, kMaxNameLength), |
780 "GenericBinaryOpStub_%s_%s%s_%s%s", | 784 "GenericBinaryOpStub_%s_%s%s_%s%s", |
781 op_name, | 785 op_name, |
782 overwrite_name, | 786 overwrite_name, |
783 (flags_ & NO_SMI_CODE_IN_STUB) ? "_NoSmiInStub" : "", | 787 (flags_ & NO_SMI_CODE_IN_STUB) ? "_NoSmiInStub" : "", |
784 args_in_registers_ ? "RegArgs" : "StackArgs", | 788 args_in_registers_ ? "RegArgs" : "StackArgs", |
785 args_reversed_ ? "_R" : ""); | 789 args_reversed_ ? "_R" : ""); |
786 return name_; | 790 return name_; |
787 } | 791 } |
788 | 792 |
789 | 793 |
(...skipping 6170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
6960 case Token::MOD: { | 6964 case Token::MOD: { |
6961 // For MOD we go directly to runtime in the non-smi case. | 6965 // For MOD we go directly to runtime in the non-smi case. |
6962 break; | 6966 break; |
6963 } | 6967 } |
6964 case Token::BIT_OR: | 6968 case Token::BIT_OR: |
6965 case Token::BIT_AND: | 6969 case Token::BIT_AND: |
6966 case Token::BIT_XOR: | 6970 case Token::BIT_XOR: |
6967 case Token::SAR: | 6971 case Token::SAR: |
6968 case Token::SHL: | 6972 case Token::SHL: |
6969 case Token::SHR: { | 6973 case Token::SHR: { |
6970 FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx); | 6974 Label non_smi_result, skip_allocation; |
6971 FloatingPointHelper::LoadFloatOperands(masm, ecx); | 6975 Label operand_conversion_failure; |
6972 | 6976 FloatingPointHelper::LoadAsIntegers( |
6973 Label skip_allocation, non_smi_result, operand_conversion_failure; | 6977 masm, |
6974 | 6978 &operand_conversion_failure); |
6975 // Reserve space for converted numbers. | |
6976 __ sub(Operand(esp), Immediate(2 * kPointerSize)); | |
6977 | |
6978 if (use_sse3_) { | |
6979 // Truncate the operands to 32-bit integers and check for | |
6980 // exceptions in doing so. | |
6981 CpuFeatures::Scope scope(SSE3); | |
6982 __ fisttp_s(Operand(esp, 0 * kPointerSize)); | |
6983 __ fisttp_s(Operand(esp, 1 * kPointerSize)); | |
6984 __ fnstsw_ax(); | |
6985 __ test(eax, Immediate(1)); | |
6986 __ j(not_zero, &operand_conversion_failure); | |
6987 } else { | |
6988 // Check if right operand is int32. | |
6989 __ fist_s(Operand(esp, 0 * kPointerSize)); | |
6990 __ fild_s(Operand(esp, 0 * kPointerSize)); | |
6991 __ FCmp(); | |
6992 __ j(not_zero, &operand_conversion_failure); | |
6993 __ j(parity_even, &operand_conversion_failure); | |
6994 | |
6995 // Check if left operand is int32. | |
6996 __ fist_s(Operand(esp, 1 * kPointerSize)); | |
6997 __ fild_s(Operand(esp, 1 * kPointerSize)); | |
6998 __ FCmp(); | |
6999 __ j(not_zero, &operand_conversion_failure); | |
7000 __ j(parity_even, &operand_conversion_failure); | |
7001 } | |
7002 | |
7003 // Get int32 operands and perform bitop. | |
7004 __ pop(ecx); | |
7005 __ pop(eax); | |
7006 switch (op_) { | 6979 switch (op_) { |
7007 case Token::BIT_OR: __ or_(eax, Operand(ecx)); break; | 6980 case Token::BIT_OR: __ or_(eax, Operand(ecx)); break; |
7008 case Token::BIT_AND: __ and_(eax, Operand(ecx)); break; | 6981 case Token::BIT_AND: __ and_(eax, Operand(ecx)); break; |
7009 case Token::BIT_XOR: __ xor_(eax, Operand(ecx)); break; | 6982 case Token::BIT_XOR: __ xor_(eax, Operand(ecx)); break; |
7010 case Token::SAR: __ sar_cl(eax); break; | 6983 case Token::SAR: __ sar_cl(eax); break; |
7011 case Token::SHL: __ shl_cl(eax); break; | 6984 case Token::SHL: __ shl_cl(eax); break; |
7012 case Token::SHR: __ shr_cl(eax); break; | 6985 case Token::SHR: __ shr_cl(eax); break; |
7013 default: UNREACHABLE(); | 6986 default: UNREACHABLE(); |
7014 } | 6987 } |
7015 if (op_ == Token::SHR) { | 6988 if (op_ == Token::SHR) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
7047 break; | 7020 break; |
7048 default: UNREACHABLE(); | 7021 default: UNREACHABLE(); |
7049 } | 7022 } |
7050 // Store the result in the HeapNumber and return. | 7023 // Store the result in the HeapNumber and return. |
7051 __ mov(Operand(esp, 1 * kPointerSize), ebx); | 7024 __ mov(Operand(esp, 1 * kPointerSize), ebx); |
7052 __ fild_s(Operand(esp, 1 * kPointerSize)); | 7025 __ fild_s(Operand(esp, 1 * kPointerSize)); |
7053 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); | 7026 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); |
7054 GenerateReturn(masm); | 7027 GenerateReturn(masm); |
7055 } | 7028 } |
7056 | 7029 |
7057 // Clear the FPU exception flag and reset the stack before calling | 7030 // Go to runtime for non-number inputs. |
7058 // the runtime system. | |
7059 __ bind(&operand_conversion_failure); | 7031 __ bind(&operand_conversion_failure); |
7060 __ add(Operand(esp), Immediate(2 * kPointerSize)); | |
7061 if (use_sse3_) { | |
7062 // If we've used the SSE3 instructions for truncating the | |
7063 // floating point values to integers and it failed, we have a | |
7064 // pending #IA exception. Clear it. | |
7065 __ fnclex(); | |
7066 } else { | |
7067 // The non-SSE3 variant does early bailout if the right | |
7068 // operand isn't a 32-bit integer, so we may have a single | |
7069 // value on the FPU stack we need to get rid of. | |
7070 __ ffree(0); | |
7071 } | |
7072 | |
7073 // SHR should return uint32 - go to runtime for non-smi/negative result. | 7032 // SHR should return uint32 - go to runtime for non-smi/negative result. |
7074 if (op_ == Token::SHR) { | 7033 if (op_ == Token::SHR) { |
7075 __ bind(&non_smi_result); | 7034 __ bind(&non_smi_result); |
7076 } | 7035 } |
7077 __ mov(eax, Operand(esp, 1 * kPointerSize)); | 7036 __ mov(eax, Operand(esp, 1 * kPointerSize)); |
7078 __ mov(edx, Operand(esp, 2 * kPointerSize)); | 7037 __ mov(edx, Operand(esp, 2 * kPointerSize)); |
7079 break; | 7038 break; |
7080 } | 7039 } |
7081 default: UNREACHABLE(); break; | 7040 default: UNREACHABLE(); break; |
7082 } | 7041 } |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
7186 // If arguments are not passed in registers remove them from the stack before | 7145 // If arguments are not passed in registers remove them from the stack before |
7187 // returning. | 7146 // returning. |
7188 if (!HasArgumentsInRegisters()) { | 7147 if (!HasArgumentsInRegisters()) { |
7189 __ ret(2 * kPointerSize); // Remove both operands | 7148 __ ret(2 * kPointerSize); // Remove both operands |
7190 } else { | 7149 } else { |
7191 __ ret(0); | 7150 __ ret(0); |
7192 } | 7151 } |
7193 } | 7152 } |
7194 | 7153 |
7195 | 7154 |
7155 // Get the integer part of a heap number. Surprisingly, all this bit twiddling | |
7156 // is faster than using the built-in instructions on floating point registers. | |
7157 // Trashes edi and ebx. Dest is ecx. Source cannot be ecx or one of the | |
7158 // trashed registers. | |
7159 void IntegerConvert(MacroAssembler* masm, | |
7160 Register source, | |
7161 Label* conversion_failure) { | |
7162 Label done, right_exponent, normal_exponent; | |
7163 Register scratch = ebx; | |
7164 Register scratch2 = edi; | |
7165 // Get exponent word. | |
7166 __ mov(scratch, FieldOperand(source, HeapNumber::kExponentOffset)); | |
7167 // Get exponent alone in scratch2. | |
7168 __ mov(scratch2, scratch); | |
7169 __ and_(scratch2, HeapNumber::kExponentMask); | |
7170 // Load ecx with zero. We use this either for the final shift or | |
7171 // for the answer. | |
7172 __ mov(ecx, Immediate(0)); | |
Lasse Reichstein
2009/12/17 16:35:50
Use xor to zero.
| |
7173 // Check whether the exponent matches a 32 bit signed int that is not a Smi. | |
Lasse Reichstein
2009/12/17 16:35:50
"is not a Smi" -> "cannot be represented as a smi"
| |
7174 // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased). This is | |
Lasse Reichstein
2009/12/17 16:35:50
"A non-smi 32-bit integer is ..."
| |
7175 // the exponent that we are fastest at and also the highest exponent we can | |
7176 // handle here. | |
7177 const uint32_t non_smi_exponent = | |
7178 (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift; | |
7179 __ cmp(Operand(scratch2), Immediate(non_smi_exponent)); | |
7180 // If we have a match of the int32-but-not-Smi exponent then skip some logic. | |
7181 __ j(equal, &right_exponent); | |
7182 // If the exponent is higher than that then go to slow case. This catches | |
7183 // numbers that don't fit in a signed int32, infinities and NaNs. | |
7184 __ j(less, &normal_exponent); | |
7185 | |
7186 // Handle a big exponent. The only reason we have this code is that the >>> | |
7187 // operator has a tendency to generate numbers with an exponent of 31. | |
7188 const uint32_t big_non_smi_exponent = | |
7189 (HeapNumber::kExponentBias + 31) << HeapNumber::kExponentShift; | |
7190 __ cmp(Operand(scratch2), Immediate(big_non_smi_exponent)); | |
7191 __ j(not_equal, conversion_failure); | |
7192 // We have the big exponent from >>>. | |
Lasse Reichstein
2009/12/17 16:35:50
... or from some other operation that gives a resu
| |
7193 // Get the top bits of the mantissa. | |
7194 __ mov(scratch2, scratch); | |
7195 __ and_(scratch2, HeapNumber::kMantissaMask); | |
7196 // Put back the implicit 1. | |
7197 __ or_(scratch2, 1 << HeapNumber::kExponentShift); | |
7198 // Shift up the mantissa bits to take up the space the exponent used to | |
7199 // take. We just orred in the implicit bit so that took care of one and | |
7200 // we want to use the full unsigned range so we subtract 1 bit from the shift | |
7201 // distance. | |
Lasse Reichstein
2009/12/17 16:35:50
Change last sentence to (something like):
We have
| |
7202 const int big_shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 1; | |
7203 __ shl(scratch2, big_shift_distance); | |
7204 // Get the second half of the double. | |
7205 __ mov(ecx, FieldOperand(source, HeapNumber::kMantissaOffset)); | |
7206 // Shift down 21 bits to get the last 11 bits. | |
Lasse Reichstein
2009/12/17 16:35:50
Explain, or replace, the constants:
21 = big_shi
| |
7207 __ shr(ecx, 32 - big_shift_distance); | |
7208 __ or_(ecx, Operand(scratch2)); | |
7209 // We have the answer in ecx, but we may need to negate it. | |
7210 __ mov(scratch2, Immediate(0)); | |
7211 __ cmp(scratch2, Operand(scratch)); | |
7212 __ j(less_equal, &done); | |
7213 __ neg(ecx); | |
Lasse Reichstein
2009/12/17 16:35:50
Slightly shorter:
test(scratch, scratch)
j(positiv
| |
7214 __ jmp(&done); | |
7215 | |
7216 // End of handling for big exponent. | |
Lasse Reichstein
2009/12/17 16:35:50
You could wrap the entire big-exponent section in
| |
7217 __ bind(&normal_exponent); | |
7218 // Exponent word in scratch, exponent part of exponent word in scratch2. | |
7219 // Zero in ecx. | |
7220 // We know the exponent is smaller than 30 (biased). If it is less than | |
7221 // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, ie | |
7222 // it rounds to zero. | |
7223 const uint32_t zero_exponent = | |
7224 (HeapNumber::kExponentBias + 0) << HeapNumber::kExponentShift; | |
Lasse Reichstein
2009/12/17 16:35:50
I think (0 + HeapNumber::kExponentBias) is more re
| |
7225 __ sub(Operand(scratch2), Immediate(zero_exponent)); | |
7226 // ecx already has a Smi zero. | |
7227 __ j(less, &done); | |
7228 | |
7229 // We have a shifted exponent between 0 and 30 in scratch2. | |
7230 __ shr(scratch2, HeapNumber::kExponentShift); | |
7231 __ mov(ecx, Immediate(30)); | |
7232 __ sub(ecx, Operand(scratch2)); | |
Lasse Reichstein
2009/12/17 16:35:50
Complex rewrite: If you make ecx one larger here (
Erik Corry
2009/12/18 09:33:36
Nice idea, but it ruins the fast case for zero shi
| |
7233 | |
7234 __ bind(&right_exponent); | |
7235 // Here ecx is the shift, scratch is the exponent word. | |
7236 // Get the top bits of the mantissa. | |
7237 __ and_(scratch, HeapNumber::kMantissaMask); | |
7238 // Put back the implicit 1. | |
7239 __ or_(scratch, 1 << HeapNumber::kExponentShift); | |
7240 // Shift up the mantissa bits to take up the space the exponent used to | |
7241 // take. We just orred in the implicit bit so that took care of one and | |
7242 // we want to leave the sign bit 0 so we subtract 2 bits from the shift | |
Lasse Reichstein
2009/12/17 16:35:50
"leave the sign bit 0" -> "leave a zero sign bit"
| |
7243 // distance. | |
7244 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2; | |
7245 __ shl(scratch, shift_distance); | |
7246 // Get the second half of the double. For some exponents we don't | |
7247 // actually need this because the bits get shifted out again, but | |
7248 // it's probably slower to test than just to do it. | |
Lasse Reichstein
2009/12/17 16:35:50
True. The load is probably from the first level ca
| |
7249 __ mov(scratch2, FieldOperand(source, HeapNumber::kMantissaOffset)); | |
7250 // Shift down 22 bits to get the last 10 bits. | |
Lasse Reichstein
2009/12/17 16:35:50
Explain or replace constants.
| |
7251 __ shr(scratch2, 32 - shift_distance); | |
7252 __ or_(scratch2, Operand(scratch)); | |
7253 // Move down according to the exponent. | |
7254 __ shr_cl(scratch2); | |
7255 // Now the unsigned answer is in scratch2. We need to move it to ecx and | |
7256 // we may need to fix the sign. | |
7257 Label negative; | |
7258 __ mov(ecx, Immediate(0)); | |
7259 __ cmp(ecx, FieldOperand(source, HeapNumber::kExponentOffset)); | |
7260 __ j(greater, &negative); | |
7261 __ mov(ecx, scratch2); | |
7262 __ jmp(&done); | |
7263 __ bind(&negative); | |
7264 __ sub(ecx, Operand(scratch2)); | |
7265 __ bind(&done); | |
7266 } | |
7267 | |
Lasse Reichstein
2009/12/17 16:35:50
Only reviewed until here.
Lasse Reichstein
2009/12/18 08:06:45
But there wasn't much more, I can see now.
| |
7268 | |
7269 // Input: edx, eax are the left and right objects of a bit op. | |
7270 // Output: eax, ecx are left and right integers for a bit op. | |
7271 void FloatingPointHelper::LoadAsIntegers(MacroAssembler* masm, | |
7272 Label* conversion_failure) { | |
7273 // Check float operands. | |
7274 Label arg1_is_object, arg2_is_object, load_arg2; | |
7275 Label done; | |
7276 | |
7277 __ test(edx, Immediate(kSmiTagMask)); | |
7278 __ j(not_zero, &arg1_is_object); | |
7279 __ sar(edx, kSmiTagSize); | |
7280 __ jmp(&load_arg2); | |
7281 | |
7282 __ bind(&arg1_is_object); | |
7283 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); | |
7284 __ cmp(ebx, Factory::heap_number_map()); | |
7285 __ j(not_equal, conversion_failure); | |
7286 // Get the untagged integer version of the edx heap number in ecx. | |
7287 IntegerConvert(masm, edx, conversion_failure); | |
7288 __ mov(edx, ecx); | |
7289 | |
7290 // Here edx has the untagged integer, eax has a Smi or a heap number. | |
7291 __ bind(&load_arg2); | |
7292 // Test if arg2 is a Smi. | |
7293 __ test(eax, Immediate(kSmiTagMask)); | |
7294 __ j(not_zero, &arg2_is_object); | |
7295 __ sar(eax, kSmiTagSize); | |
7296 __ mov(ecx, eax); | |
7297 __ jmp(&done); | |
7298 | |
7299 __ bind(&arg2_is_object); | |
7300 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); | |
7301 __ cmp(ebx, Factory::heap_number_map()); | |
7302 __ j(not_equal, conversion_failure); | |
7303 // Get the untagged integer version of the eax heap number in ecx. | |
7304 IntegerConvert(masm, eax, conversion_failure); | |
7305 __ bind(&done); | |
7306 __ mov(eax, edx); | |
7307 } | |
7308 | |
7309 | |
7196 void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm, | 7310 void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm, |
7197 Register number) { | 7311 Register number) { |
7198 Label load_smi, done; | 7312 Label load_smi, done; |
7199 | 7313 |
7200 __ test(number, Immediate(kSmiTagMask)); | 7314 __ test(number, Immediate(kSmiTagMask)); |
7201 __ j(zero, &load_smi, not_taken); | 7315 __ j(zero, &load_smi, not_taken); |
7202 __ fld_d(FieldOperand(number, HeapNumber::kValueOffset)); | 7316 __ fld_d(FieldOperand(number, HeapNumber::kValueOffset)); |
7203 __ jmp(&done); | 7317 __ jmp(&done); |
7204 | 7318 |
7205 __ bind(&load_smi); | 7319 __ bind(&load_smi); |
(...skipping 1252 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
8458 __ add(Operand(dest), Immediate(2)); | 8572 __ add(Operand(dest), Immediate(2)); |
8459 } | 8573 } |
8460 __ sub(Operand(count), Immediate(1)); | 8574 __ sub(Operand(count), Immediate(1)); |
8461 __ j(not_zero, &loop); | 8575 __ j(not_zero, &loop); |
8462 } | 8576 } |
8463 | 8577 |
8464 | 8578 |
8465 #undef __ | 8579 #undef __ |
8466 | 8580 |
8467 } } // namespace v8::internal | 8581 } } // namespace v8::internal |
OLD | NEW |