summaryrefslogtreecommitdiffstats
Commit message (Collapse)AuthorAgeFilesLines
* i965/fs: fix copy/constant propagation regioning checksIago Toral Quiroga2016-05-161-8/+20
| | | | | | | | | | | | | | | | | | | | | | | | | | | We were not accounting for subreg_offset in the check for the start of the region. Also, fs_reg::regs_read() already takes the stride into account, so we should not multiply its result by the stride again. This was making copy-propagation fail to copy-propagate cases that would otherwise be safe to copy-propagate. Again, this was observed in fp64 code, since there we use stride > 1 often. v2 (Sam): - Rename function and add comment (Jason, Curro). - Assert that register files and number are the same (Jason). - Fix code to take into account the assumption that src.subreg_offset is strictly less than the reg_offset unit (Curro). - Don't pass the registers by value to the function, use 'const fs_reg &' instead (Curro). - Remove obsolete comment in the commit log (Curro). v3 (Sam): - Remove the assert and put the condition in the return (Curro). - Fix function name (Curro). Reviewed-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Francisco Jerez <currojerez@riseup.net>
* i965/fs: fix copy propagation from load payloadIago Toral Quiroga2016-05-161-1/+1
| | | | | | | | We were not considering the case where the load payload is writing to a destination with a reg_offset > 0. Reviewed-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Francisco Jerez <currojerez@riseup.net>
* i965/fs: fix copy propagation of partially invalidated entriesIago Toral Quiroga2016-05-161-8/+27
| | | | | | | | | | | | | | | We were not invalidating entries with a src that reads more than one register when we find writes that overwrite any register read by entry->src after the first. This leads to incorrect copy propagation because we re-use entries from the ACP that have been partially invalidated. Same thing for entries with a dst that writes to more than one register. v2 (Sam): - Improve code by defining regions_overlap() and using it instead of a loop (Curro). Reviewed-by: Francisco Jerez <currojerez@riseup.net> Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
* i965/fs: Reindent register offset calculation of try_copy_propagate().Francisco Jerez2016-05-161-23/+23
| | | | Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
* i965/fs: Simplify and fix register offset calculation of try_copy_propagate().Francisco Jerez2016-05-161-31/+9
| | | | | | | | | | | | | | | try_copy_propagate() was special-casing UNIFORM registers (the BAD_FILE, ARF and FIXED_GRF cases are dead, see the assertion at the top of the function) and then failing to take into account the possibility of the instruction reading from a non-zero offset of the destination of the copy. The VGRF/ATTR handling takes it into account correctly, and there is no reason we couldn't use the exact same logic for the UNIFORM file aside from the fact that uniforms represent reg_offset in different units. We can work around that easily by defining an additional constant with the right unit reg_offset is expressed in. Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
* i965/fs: disallow type change in copy-propagation if types have different sizesIago Toral Quiroga2016-05-161-1/+8
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Because the semantics of source modifiers are type-dependent, the type of the original source of the copy must be kept unmodified while propagating it into some instruction, which implies that we need to have the guarantee that the meaning of the instruction is going to remain the same after we have changed the types. Whenthe size of the new type is different from the size of the old type the new and old instructions cannot possibly be equivalent because the new instruction will be reading more data than the old one was. Prevents that we turn this: load_payload(8) vgrf17:DF, |vgrf4+0.0|:DF 1sthalf mov(8) vgrf18:DF, vgrf17:DF 1sthalf load_payload(8) vgrf5:DF, vgrf18:DF, vgrf20:DF NoMask 1sthalf WE_all load_payload(8) vgrf21:UD, vgrf5+0.4<2>:UD 1sthalf mov(8) vgrf22:UD, vgrf21:UD 1sthalf into: load_payload(8) vgrf17:DF, |vgrf4+0.0|:DF 1sthalf mov(8) vgrf18:DF, |vgrf4+0.0|:DF 1sthalf load_payload(8) vgrf5:DF, |vgrf4+0.0|:DF, |vgrf4+2.0|:DF NoMask 1sthalf WE_all load_payload(8) vgrf21:UD, vgrf5+0.4<2>:UD 1sthalf mov(8) vgrf22:DF, |vgrf4+0.4|<2>:DF 1sthalf where the semantics of the last instruccion have changed. v2 (Curro): - Update commit log and add comment to explain the problem better. - Simplify the condition. Reviewed-by: Francisco Jerez <currojerez@riseup.net> Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
* i965/fs: Fix copy propagation of load payload for double operandsIago Toral Quiroga2016-05-161-1/+3
| | | | | | | | | | | | | | | | Specifically, consider the size of the data type of the operand to compute the number of registers written. v2 (Sam): - Fix line width (Jordan). - Add an assert (Jordan). - Use REG_SIZE in the calculation of regs_written (Curro) v3 (Sam): - Fix assert and calculation of regs_written (Curro). Reviewed-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Francisco Jerez <currojerez@riseup.net>
* i965/fs: Fix propagation of copies with strided source.Francisco Jerez2016-05-161-10/+20
| | | | | | | | | | | | This has likely been broken since we started propagating copies not matching the offset of the instruction exactly (1728e74957a62b1b4b9fbb62a7de2c12b77c8a75). The copy source stride needs to be taken into account to find out the offset at the origin that corresponds to the offset at the destination of the copy which is being read by the instruction. This has led to program miscompilation on both my SIMD32 branch and Igalia's FP64 branch. Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
* i965/fs: fix subreg_offset overflow in byte_offset()Iago Toral Quiroga2016-05-161-4/+6
| | | | | | | | | | | This can happen if the register already has a non-zero subreg_offset when byte_offset() is called. v2 (Sam): - Refactor byte_offset() (Jordan). Signed-off-by: Samuel Iglesias Gonsálvez <siglesias@igalia.com> Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
* i965: Fix JIP to skip over sibling do...while loops.Kenneth Graunke2016-05-161-1/+6
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | We've apparently always been botching JIP for sequences such as: do cmp.f0.0 ... (+f0.0) break ... do ... while ... while Because the "do" instruction doesn't actually exist, the inner "while" is at the same depth as the "break". brw_find_next_block_end() thus mistook the inner "while" as the end of the loop containing the "break", and set the "break" to point to the wrong place. Only "while" instructions that jump before our instruction are relevant. We need to ignore the rest, as they're sibling control flow nodes (or children, but this was already handled by the depth == 0 check). See also commit 1ac1581f3889d5f7e6e231c05651f44fbd80f0b6. This prevents channel masks from being screwed up, and fixes GPU hangs(*) in dEQP-GLES31.functional.shaders.multisample_interpolation. interpolate_at_sample.centroid_qualified.multisample_texture_16. The test ended up executing code with no channels enabled, and that code contained FIND_LIVE_CHANNEL, which returned 8 (out of range for a SIMD8 program), which then was used in indirect GRF addressing, which randomly got a boolean value (0xFFFFFFFF), interpreted it as a sample ID, OR'd it into an indirect send message descriptor, which corrupted the message length, sending a pixel interpolator message with mlen 15, which is illegal. Whew :) (*) Technically, the test doesn't GPU hang currently, but only because another bug prevents it from issuing pixel interpolator messages entirely...with that fixed, it hangs. Cc: mesa-stable@lists.freedesktop.org Signed-off-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Francisco Jerez <currojerez@riseup.net>
* i965: Make a "does this while jump before our instruction?" helper.Kenneth Graunke2016-05-161-4/+12
| | | | | | | | I need to use this in an additional place. Cc: mesa-stable@lists.freedesktop.org Signed-off-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Francisco Jerez <currojerez@riseup.net>
* i965: Send the minimal number of STATE_BASE_ADDRESS packets.Kenneth Graunke2016-05-169-29/+12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | STATE_BASE_ADDRESS stalls the whole pipeline, and the documentation cautions us to emit it as little as possible for better performance. We recently put some hacks in BLORP to try and avoid emitting it if it was already set correctly. However, this wasn't quite minimal: if BLORP is the first operation (i.e. glClear()), then it would emit it, and subsequent draw calls would emit it again. This caused a small drop in performance in GPUTest Triangle when switching from Meta to BLORP. Unlike most packets, STATE_BASE_ADDRESS isn't influenced by GL state: it needs to be emitted once per batch, before most other commands, or whenever we change the program cache BO. It's also valid in both the 3D and compute pipelines, which makes it even more unique. This patch removes it from the atom mechanism and instead directly calls it as part of every draw, compute dispatch, or BLORP operation. We introduce a new flag indicating that STATE_BASE_ADDRESS has already been emitted this batch, and if so, skip doing it again. When we make a new program cache BO, we simply reset the flag, so the next operation will emit it again. When we flush/reset the batch, we reset the flag. This guarantees that we'll emit STATE_BASE_ADDRESS only when we have to. It's also less code than the old atom mechanism. Signed-off-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
* i965: Combine Gen4-7 and Gen8+ state base address emitters.Kenneth Graunke2016-05-168-97/+51
| | | | | | | | We're about to start calling it directly, and this means the callers won't have to think about generations. Signed-off-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
* i965: Move Gen4-5 programs to brw_upload_programs() too.Kenneth Graunke2016-05-165-56/+51
| | | | | | | | This way all the programs are in one place again, and it also should make some future STATE_BASE_ADDRESS related changes possible. Signed-off-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
* i965: Mark brw const in brw_state_dirty and callers.Kenneth Graunke2016-05-165-5/+6
| | | | | Signed-off-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
* glsl: Don't do constant propagation in opt_constant_folding.Kenneth Graunke2016-05-152-0/+21
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | opt_constant_folding is supposed to fold trees of constants into a single constant. Surprisingly, it was also propagating constant values from variables into expression trees - even when the result couldn't be folded together. This is opt_constant_propagation's job. The ir_dereference_variable::constant_expression_value() method returns a clone of var->constant_value. So we would replace the dereference with a constant, propagating it into the tree. Skip over ir_dereference_variable to avoid this surprising behavior. However, add code to explicitly continue doing it in the constant propagation pass, as it's useful to do so. shader-db statistics on Broadwell: total instructions in shared programs: 8905349 -> 8905126 (-0.00%) instructions in affected programs: 30100 -> 29877 (-0.74%) helped: 93 HURT: 20 total cycles in shared programs: 71017030 -> 71015944 (-0.00%) cycles in affected programs: 132456 -> 131370 (-0.82%) helped: 54 HURT: 45 The only hurt programs are by a single instruction, while the helped ones are helped by 1-4 instructions. Signed-off-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
* glsl: Avoid excess tree walking when folding ir_dereference_arrays.Kenneth Graunke2016-05-151-0/+6
| | | | | | | | | | | | | | | | | | | If an ir_dereference_array has non-constant components, there's no point in trying to evaluate its value (which involves walking down the tree and possibly allocating memory for portions of the subtree which are constant). This also removes convoluted tree walking in opt_constant_folding(), which tries to fold constants while walking up the tree. No need to walk down, then up, then down again. We did this for swizzles and expressions already, but I was lazy back in the day and didn't do this for ir_dereference_array. No change in shader-db. Signed-off-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
* glsl: Consolidate duplicate copies of constant folding.Kenneth Graunke2016-05-153-32/+19
| | | | | | | | | | We could probably clean this up more (maybe make it a method), but at least there's only one copy of this code now, and that's a start. No change in shader-db. Signed-off-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
* glsl: Remove bonus tree walking in opt_constant_folding().Kenneth Graunke2016-05-151-2/+0
| | | | | | | | | | | | | | It looks like this was missed when converting opt_constant_folding() from a hierarchical visitor to an rvalue visitor in 6606fde3. ir_rvalue_visitor already processes values on the way back up the tree, so we will have already visited every child node. There's no point in doing it again. No change in shader-db. Signed-off-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
* glsl: Make opt_constant_variable() bail in useless cases.Kenneth Graunke2016-05-151-0/+7
| | | | | | | | | | | | | | | The pass ultimately skips over any entries with assignment_count != 1, so there's no need to do further work once we've determined that there are multiple assignments. The constant value could be a large array (i.e. uvec4[327]), at which point skipping the constant_expression_value() call (and the clone() call within) can save us piles of memory. No change in shader-db. Signed-off-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
* i965: Flip interpolateAtOffset's y offset when necessary.Kenneth Graunke2016-05-152-3/+8
| | | | | | | | | | | Fixes 4 dEQP-GLES31.functional.shaders.multisample_interpolation tests: - interpolate_at_offset.no_qualifiers.default_framebuffer - interpolate_at_offset.centroid_qualifier.default_framebuffer - interpolate_at_offset.sample_qualifier.default_framebuffer - interpolate_at_offset.array_element.default_framebuffer Signed-off-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
* nir: Add a nir->info.uses_interp_var_at_offset flag.Kenneth Graunke2016-05-153-0/+10
| | | | | | | | | I've added this to nir_gather_info(), but also to glsl_to_nir() as a temporary measure, since the i965 GL driver today doesn't use nir_gather_info() yet. Signed-off-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
* glsl: Drop bad ASSERT_TRUE in gl_CullDistance link_varyings test.Kenneth Graunke2016-05-151-6/+6
| | | | | | | | | | | | I don't know what the intention was here, but this function returns void. We can't assert anything about its return value. Fixes "make check" failures. v2: Also fix prototype for the function (caught by Jordan). Signed-off-by: Kenneth Graunke <kenneth@whitecape.org> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
* clover: Handle PIPE_SHADER_IR_NIR in switchJan Vesely2016-05-151-1/+2
| | | | | Signed-off-by: Jan Vesely <jan.vesely@rutgers.edu> Reviewed-by: Francisco Jerez <currojerez@riseup.net>
* freedreno/ir3: small standalone compiler cleanupRob Clark2016-05-151-2/+1
| | | | | | Don't hard-code the gpu-id anymore. Signed-off-by: Rob Clark <robclark@freedesktop.org>
* nir: forward-declare 'struct gl_shader_program'Rob Clark2016-05-151-1/+2
| | | | | | | | Drop extra #include which is otherwise unneeded (and makes this header difficult to include from outside of src/mesa). Signed-off-by: Rob Clark <robclark@freedesktop.org> Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
* nir: return progress from lower_idivRob Clark2016-05-152-7/+16
| | | | | | | | | | With algebraic-opt support for lowering div to shift, the driver would like to be able to run this pass *after* the main opt-loop, and then conditionally re-run the opt-loop if this pass actually lowered some- thing. Signed-off-by: Rob Clark <robclark@freedesktop.org> Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
* freedreno/ir3: lower fdivRob Clark2016-05-151-0/+1
| | | | | | | Not sure how we didn't hit this already, but since we want fdiv converted into mul + rcp, we should set this. Signed-off-by: Rob Clark <robclark@freedesktop.org>
* freedreno/ir3: handle VARYING_SLOT_PNTCRob Clark2016-05-151-0/+12
| | | | | | | In the glsl->tgsi path, this already gets translated to VAR8, which matches up with rasterizer->sprite_coord_enable. Signed-off-by: Rob Clark <robclark@freedesktop.org>
* freedreno/ir3: disable TGSI specific hacks in nir caseRob Clark2016-05-154-2/+7
| | | | | | | When we got NIR directly from state tracker (vs using tgsi_to_nir) we need to realize this and skip some TGSI specific hacks. Signed-off-by: Rob Clark <robclark@freedesktop.org>
* freedreno/ir3: add support for NIR as preferred IRRob Clark2016-05-156-18/+61
| | | | | | For now under debug flag, since only suitable for debugging/testing. Signed-off-by: Rob Clark <robclark@freedesktop.org>
* nir: fix comment typo about f2d/d2fRob Clark2016-05-151-2/+2
| | | | | Signed-off-by: Rob Clark <robclark@freedesktop.org> Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
* nv50/ir: avoid asserts when the state tracker feeds us bogus inputsIlia Mirkin2016-05-151-12/+48
| | | | | | | | | | | | | | | | | | INTERP is defined (by me) to have to have a INPUT source. However the state tracker does not always obey this. This happens due to varying packing logic introducing additional mov's which can't always be undone. Instead of just giving up, we instead try harder to find the original input. This won't always be possible, for example with indirect accesses. There's not much we can (easily) do about that though. This fixes the remaining interpolateAt* failures in dEQP: dEQP-GLES31.functional.shaders.multisample_interpolation.interpolate_at* some of which were asserting due to INTERP_* being passed a non-input. Signed-off-by: Ilia Mirkin <imirkin@alum.mit.edu> Reviewed-by: Samuel Pitoiset <samuel.pitoiset@gmail.com>
* nvc0: don't try to go through the push path for indirect drawsIlia Mirkin2016-05-151-1/+2
| | | | | | | | | | | | | | This fixes dEQP-GLES31.functional.draw_indirect.draw_elements_indirect.*.default_attribute These tests were causing a const vbo to be set up, and were small enough draws that the logic was trying to go via the push path (which emits data directly into the cmd stream rather than uploading a user vbo). Signed-off-by: Ilia Mirkin <imirkin@alum.mit.edu> Cc: mesa-stable@lists.freedesktop.org Reviewed-by: Samuel Pitoiset <samuel.pitoiset@gmail.com>
* nvc0/ir: make sure to align the second arg of TXD to 4, as we do for TEXIlia Mirkin2016-05-151-0/+14
| | | | | | | | | | | | | This was handled in handleTEX(), however the way the logic works, those extra arguments aren't added on by then, so it did nothing. Instead we must duplicate that bit here. GK110 appears to complain about MISALIGNED_GPR, however it's reasonable to believe that GK104 has the same requirements. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=95403 Signed-off-by: Ilia Mirkin <imirkin@alum.mit.edu> Cc: mesa-stable@lists.freedesktop.org Reviewed-by: Samuel Pitoiset <samuel.pitoiset@gmail.com>
* nv50,nvc0: add support for cull distancesTobias Klausmann2016-05-1512-13/+35
| | | | | | | | | | | Cull distances are just a special case of clip distances as far as the hardware is concerned. Make sure that the relevant "planes" are enabled, and flip the clip mode to cull for those. Signed-off-by: Tobias Klausmann <tobias.johannes.klausmann@mni.thm.de> [imirkin: add enables on nvc0, add nv50 support] Signed-off-by: Ilia Mirkin <imirkin@alum.mit.edu> Reviewed-by: Tobias Klausmann <tobias.johannes.klausmann@mni.thm.de>
* st/mesa: disable cull distance for nowIlia Mirkin2016-05-151-1/+1
| | | | | | | | | The pass that st/mesa relies on to combine clip and cull distances has been reverted, so we can't expose ARB_cull_distance until that is resolved. Signed-off-by: Ilia Mirkin <imirkin@alum.mit.edu> Reviewed-by: Marek Olšák <marek.olsak@amd.com>
* i965: Use blorp for all clearsJason Ekstrand2016-05-145-634/+0
| | | | | | | We used to use a meta path on gen8 but we haven't since c7cf17ae758. We might as well delete the meta path since blorp works on all gens. Reviewed-by: Topi Pohjolainen <topi.pohjolainen@intel.com>
* i965: Use blorp for all stencil blitsJason Ekstrand2016-05-145-547/+1
| | | | | | | We used to use a meta path because blorp didn't support 16x MSAA. Now it does, so we don't need the meta paths anymore. Reviewed-by: Topi Pohjolainen <topi.pohjolainen@intel.com>
* i965: Use blorp for all updownsample blitsJason Ekstrand2016-05-145-178/+10
| | | | | | | We used to use a meta path because blorp didn't support 16x MSAA. Now it does, so we don't need the meta paths anymore. Reviewed-by: Topi Pohjolainen <topi.pohjolainen@intel.com>
* i965/blorp: Add support for 16x MSAAJason Ekstrand2016-05-141-1/+76
| | | | Reviewed-by: Topi Pohjolainen <topi.pohjolainen@intel.com>
* i965: move brw_meta_set_fast_clear_color to brw_meta_util.cJason Ekstrand2016-05-142-75/+74
| | | | Reviewed-by: Topi Pohjolainen <topi.pohjolainen@intel.com>
* i965; Move brw_meta_get_*_rect to brw_meta_util.cJason Ekstrand2016-05-142-180/+180
| | | | Reviewed-by: Topi Pohjolainen <topi.pohjolainen@intel.com>
* i965: Move brw_is_color_fast_clear_compatible to brw_meta_utilJason Ekstrand2016-05-142-59/+61
| | | | Reviewed-by: Topi Pohjolainen <topi.pohjolainen@intel.com>
* i965: Move brw_get_rb_for_slice to brw_meta_utilJason Ekstrand2016-05-144-49/+52
| | | | Reviewed-by: Topi Pohjolainen <topi.pohjolainen@intel.com>
* i965/blorp: Get rid of the blorp_prog_data_int() helperJason Ekstrand2016-05-144-26/+2
| | | | | | | | | The helper was initially created to allow us to set reasonable defaults as we mutated the brw_blorp_prog_data structure in preparation for NIR. Now that everything is going through brw_blorp_compile_nir_shader() which fully fills out the brw_blorp_prog_data structure, we don't need the helper. Reviewed-by: Topi Pohjolainen <topi.pohjolainen@intel.com>
* i965/blorp: Delete the old blorp shader emit codeJason Ekstrand2016-05-148-1664/+8
| | | | Reviewed-by: Topi Pohjolainen <topi.pohjolainen@intel.com>
* i965/blorp: Stop doing f2i(i2f(sample_id))Jason Ekstrand2016-05-141-13/+23
| | | | | | | | NIR gets kind of awkward when you have a 3-component vector with two floats and one int. This led to us accidentally going through float for the sample index. It doesn't hurt anything but it also isn't needed. Reviewed-by: Topi Pohjolainen <topi.pohjolainen@intel.com>
* i965/blorp: Refactor coordinate mungingJason Ekstrand2016-05-141-30/+29
| | | | | | | The original code-flow tried to map original blorp. This puts things more where they belong and simplifies some of the logic. Reviewed-by: Topi Pohjolainen <topi.pohjolainen@intel.com>
* i965/blorp: Add bilinear blending support to the NIR pathJason Ekstrand2016-05-141-6/+114
| | | | Reviewed-by: Topi Pohjolainen <topi.pohjolainen@intel.com>