зеркало из https://github.com/mozilla/pjs.git
Bug 512181 - nanojit: rework TMFLAGS=assembly,regalloc,activation. r=edwsmith.
--HG-- extra : convert_revision : 43e64a1135f17761aad95ee7ce2d1692aa937579
This commit is contained in:
Родитель
67ada8c5e1
Коммит
a4ebd385c0
|
@ -81,6 +81,7 @@ namespace nanojit
|
|||
(void)logc;
|
||||
verbose_only( _logc = logc; )
|
||||
verbose_only( _outputCache = 0; )
|
||||
verbose_only( outline[0] = '\0'; )
|
||||
verbose_only( outlineEOL[0] = '\0'; )
|
||||
verbose_only( outputAddr = false; )
|
||||
|
||||
|
@ -142,6 +143,9 @@ namespace nanojit
|
|||
vicIns->setReg(UnknownReg);
|
||||
|
||||
// Restore vicIns.
|
||||
verbose_only( if (_logc->lcbits & LC_Assembly) {
|
||||
setOutputForEOL(" <= restore %s",
|
||||
_thisfrag->lirbuf->names->formatRef(vicIns)); } )
|
||||
asm_restore(vicIns, vicIns->resv(), r);
|
||||
|
||||
// r ends up staying active, but the LIns defining it changes.
|
||||
|
@ -495,9 +499,9 @@ namespace nanojit
|
|||
{
|
||||
int d = disp(ins);
|
||||
Register r = ins->getReg();
|
||||
verbose_only( if (d && (_logc->lcbits & LC_RegAlloc)) {
|
||||
outputForEOL(" <= spill %s",
|
||||
_thisfrag->lirbuf->names->formatRef(ins)); } )
|
||||
verbose_only( if (d && (_logc->lcbits & LC_Assembly)) {
|
||||
setOutputForEOL(" <= spill %s",
|
||||
_thisfrag->lirbuf->names->formatRef(ins)); } )
|
||||
asm_spill(r, d, pop, ins->isQuad());
|
||||
}
|
||||
|
||||
|
@ -554,6 +558,9 @@ namespace nanojit
|
|||
vic->setReg(UnknownReg);
|
||||
|
||||
// Restore vic.
|
||||
verbose_only( if (_logc->lcbits & LC_Assembly) {
|
||||
setOutputForEOL(" <= restore %s",
|
||||
_thisfrag->lirbuf->names->formatRef(vic)); } )
|
||||
asm_restore(vic, vic->resv(), r);
|
||||
}
|
||||
|
||||
|
@ -949,6 +956,17 @@ namespace nanojit
|
|||
if (!required)
|
||||
continue;
|
||||
|
||||
#ifdef NJ_VERBOSE
|
||||
// Output the register post-state and/or activation post-state.
|
||||
// Because asm output comes in reverse order, doing it now means
|
||||
// it is printed after the LIR and asm, exactly when the
|
||||
// post-state should be shown.
|
||||
if ((_logc->lcbits & LC_Assembly) && (_logc->lcbits & LC_Activation))
|
||||
printActivationState();
|
||||
if ((_logc->lcbits & LC_Assembly) && (_logc->lcbits & LC_RegAlloc))
|
||||
printRegState();
|
||||
#endif
|
||||
|
||||
LOpcode op = ins->opcode();
|
||||
switch(op)
|
||||
{
|
||||
|
@ -1557,8 +1575,6 @@ namespace nanojit
|
|||
|
||||
void Assembler::arFree(uint32_t idx)
|
||||
{
|
||||
verbose_only( printActivationState(" >FP"); )
|
||||
|
||||
AR &ar = _activation;
|
||||
LIns *i = ar.entry[idx];
|
||||
NanoAssert(i != 0);
|
||||
|
@ -1569,19 +1585,46 @@ namespace nanojit
|
|||
}
|
||||
|
||||
#ifdef NJ_VERBOSE
|
||||
void Assembler::printActivationState(const char* what)
|
||||
void Assembler::printRegState()
|
||||
{
|
||||
if (!(_logc->lcbits & LC_Activation))
|
||||
return;
|
||||
|
||||
char* s = &outline[0];
|
||||
VMPI_memset(s, ' ', 45); s[45] = '\0';
|
||||
VMPI_memset(s, ' ', 26); s[26] = '\0';
|
||||
s += VMPI_strlen(s);
|
||||
VMPI_sprintf(s, "%s", what);
|
||||
VMPI_sprintf(s, "RR");
|
||||
s += VMPI_strlen(s);
|
||||
|
||||
for (Register r = FirstReg; r <= LastReg; r = nextreg(r)) {
|
||||
LIns *ins = _allocator.getActive(r);
|
||||
if (ins) {
|
||||
NanoAssertMsg(!_allocator.isFree(r),
|
||||
"Coding error; register is both free and active! " );
|
||||
const char* n = _thisfrag->lirbuf->names->formatRef(ins);
|
||||
|
||||
if (ins->isop(LIR_param) && ins->paramKind()==1 &&
|
||||
r == Assembler::savedRegs[ins->paramArg()])
|
||||
{
|
||||
// dont print callee-saved regs that arent used
|
||||
continue;
|
||||
}
|
||||
|
||||
const char* rname = ins->isQuad() ? fpn(r) : gpn(r);
|
||||
VMPI_sprintf(s, " %s(%s)", rname, n);
|
||||
s += VMPI_strlen(s);
|
||||
}
|
||||
}
|
||||
output();
|
||||
}
|
||||
|
||||
void Assembler::printActivationState()
|
||||
{
|
||||
char* s = &outline[0];
|
||||
VMPI_memset(s, ' ', 26); s[26] = '\0';
|
||||
s += VMPI_strlen(s);
|
||||
VMPI_sprintf(s, "AR");
|
||||
s += VMPI_strlen(s);
|
||||
|
||||
int32_t max = _activation.tos < NJ_MAX_STACK_ENTRY ? _activation.tos : NJ_MAX_STACK_ENTRY;
|
||||
for(int32_t i = _activation.lowwatermark; i < max; i++) {
|
||||
for (int32_t i = _activation.lowwatermark; i < max; i++) {
|
||||
LIns *ins = _activation.entry[i];
|
||||
if (ins) {
|
||||
const char* n = _thisfrag->lirbuf->names->formatRef(ins);
|
||||
|
@ -1605,7 +1648,7 @@ namespace nanojit
|
|||
}
|
||||
s += VMPI_strlen(s);
|
||||
}
|
||||
output(&outline[0]);
|
||||
output();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1625,7 +1668,6 @@ namespace nanojit
|
|||
int32_t start = ar.lowwatermark;
|
||||
int32_t i = 0;
|
||||
NanoAssert(start>0);
|
||||
verbose_only( printActivationState(" <FP"); )
|
||||
|
||||
if (size == 1) {
|
||||
// easy most common case -- find a hole, or make the frame bigger
|
||||
|
@ -1903,74 +1945,43 @@ namespace nanojit
|
|||
}
|
||||
|
||||
#ifdef NJ_VERBOSE
|
||||
// "outline" must be able to hold the output line in addition to the
|
||||
// outlineEOL buffer, which is concatenated onto outline just before it
|
||||
// is printed.
|
||||
char Assembler::outline[8192];
|
||||
char Assembler::outlineEOL[512];
|
||||
|
||||
void Assembler::outputForEOL(const char* format, ...)
|
||||
void Assembler::output()
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
// The +1 is for the terminating NUL char.
|
||||
VMPI_strncat(outline, outlineEOL, sizeof(outline)-(strlen(outline)+1));
|
||||
|
||||
if (_outputCache) {
|
||||
char* str = new (alloc) char[VMPI_strlen(outline)+1];
|
||||
VMPI_strcpy(str, outline);
|
||||
_outputCache->insert(str);
|
||||
} else {
|
||||
_logc->printf("%s\n", outline);
|
||||
}
|
||||
|
||||
outline[0] = '\0';
|
||||
outlineEOL[0] = '\0';
|
||||
vsprintf(outlineEOL, format, args);
|
||||
}
|
||||
|
||||
void Assembler::outputf(const char* format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
outline[0] = '\0';
|
||||
vsprintf(outline, format, args);
|
||||
output();
|
||||
}
|
||||
|
||||
// Format the output string and remember the number of characters
|
||||
// that were written.
|
||||
uint32_t outline_len = vsprintf(outline, format, args);
|
||||
void Assembler::setOutputForEOL(const char* format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
// Add the EOL string to the output, ensuring that we leave enough
|
||||
// space for the terminating NULL character, then reset it so it
|
||||
// doesn't repeat on the next outputf.
|
||||
VMPI_strncat(outline, outlineEOL, sizeof(outline)-(outline_len+1));
|
||||
outlineEOL[0] = '\0';
|
||||
|
||||
output(outline);
|
||||
}
|
||||
|
||||
void Assembler::output(const char* s)
|
||||
{
|
||||
if (_outputCache)
|
||||
{
|
||||
char* str = new (alloc) char[VMPI_strlen(s)+1];
|
||||
VMPI_strcpy(str, s);
|
||||
_outputCache->insert(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logc->printf("%s\n", s);
|
||||
}
|
||||
}
|
||||
|
||||
void Assembler::output_asm(const char* s)
|
||||
{
|
||||
if (!(_logc->lcbits & LC_Assembly))
|
||||
return;
|
||||
|
||||
// Add the EOL string to the output, ensuring that we leave enough
|
||||
// space for the terminating NULL character, then reset it so it
|
||||
// doesn't repeat on the next outputf.
|
||||
VMPI_strncat(outline, outlineEOL, sizeof(outline)-(strlen(outline)+1));
|
||||
outlineEOL[0] = '\0';
|
||||
|
||||
output(s);
|
||||
}
|
||||
|
||||
char* Assembler::outputAlign(char *s, int col)
|
||||
{
|
||||
int len = (int)VMPI_strlen(s);
|
||||
int add = ((col-len)>0) ? col-len : 1;
|
||||
VMPI_memset(&s[len], ' ', add);
|
||||
s[col] = '\0';
|
||||
return &s[col];
|
||||
vsprintf(outlineEOL, format, args);
|
||||
}
|
||||
#endif // NJ_VERBOSE
|
||||
|
||||
|
|
|
@ -189,25 +189,41 @@ namespace nanojit
|
|||
friend class VerboseBlockReader;
|
||||
public:
|
||||
#ifdef NJ_VERBOSE
|
||||
static char outline[8192];
|
||||
static char outlineEOL[512]; // string to be added to the end of the line
|
||||
static char* outputAlign(char* s, int col);
|
||||
|
||||
void outputForEOL(const char* format, ...);
|
||||
void output(const char* s);
|
||||
void outputf(const char* format, ...);
|
||||
void output_asm(const char* s);
|
||||
|
||||
bool outputAddr, vpad[3]; // if outputAddr=true then next asm instr. will include address in output
|
||||
void printActivationState(const char* what);
|
||||
|
||||
// Log controller object. Contains what-stuff-should-we-print
|
||||
// bits, and a sink function for debug printing.
|
||||
LogControl* _logc;
|
||||
// Buffer for holding text as we generate it in reverse order.
|
||||
StringList* _outputCache;
|
||||
|
||||
// Log controller object. Contains what-stuff-should-we-print
|
||||
// bits, and a sink function for debug printing
|
||||
LogControl* _logc;
|
||||
// Outputs the format string and 'outlineEOL', and resets
|
||||
// 'outline' and 'outlineEOL'.
|
||||
void outputf(const char* format, ...);
|
||||
|
||||
private:
|
||||
// Buffer used in most of the output function. It must big enough
|
||||
// to hold both the output line and the 'outlineEOL' buffer, which
|
||||
// is concatenated onto 'outline' just before it is printed.
|
||||
static char outline[8192];
|
||||
// Buffer used to hold extra text to be printed at the end of some
|
||||
// lines.
|
||||
static char outlineEOL[512];
|
||||
// If outputAddr=true the next asm instruction output will
|
||||
// be prepended with its address.
|
||||
bool outputAddr, vpad[3];
|
||||
|
||||
// Outputs 'outline' and 'outlineEOL', and resets them both.
|
||||
// Output goes to '_outputCache' if it's non-NULL, or is printed
|
||||
// directly via '_logc'.
|
||||
void output();
|
||||
|
||||
// Sets 'outlineEOL'.
|
||||
void setOutputForEOL(const char* format, ...);
|
||||
|
||||
void printRegState();
|
||||
void printActivationState();
|
||||
#endif // NJ_VERBOSE
|
||||
|
||||
public:
|
||||
#ifdef VTUNE
|
||||
avmplus::CodegenLIR *cgen;
|
||||
#endif
|
||||
|
@ -388,10 +404,10 @@ namespace nanojit
|
|||
|
||||
// since we generate backwards the depth is negative
|
||||
inline void fpu_push() {
|
||||
debug_only( ++_fpuStkDepth; /*char foo[8]= "FPUSTK0"; foo[6]-=_fpuStkDepth; output_asm(foo);*/ NanoAssert(_fpuStkDepth<=0); )
|
||||
debug_only( ++_fpuStkDepth; NanoAssert(_fpuStkDepth<=0); )
|
||||
}
|
||||
inline void fpu_pop() {
|
||||
debug_only( --_fpuStkDepth; /*char foo[8]= "FPUSTK0"; foo[6]-=_fpuStkDepth; output_asm(foo);*/ NanoAssert(_fpuStkDepth<=0); )
|
||||
debug_only( --_fpuStkDepth; NanoAssert(_fpuStkDepth<=0); )
|
||||
}
|
||||
#endif
|
||||
avmplus::Config &config;
|
||||
|
|
|
@ -1868,7 +1868,7 @@ namespace nanojit
|
|||
|
||||
case LIR_qjoin:
|
||||
VMPI_sprintf(s, "%s (%s), %s", lirNames[op],
|
||||
formatIns(i->oprnd1()),
|
||||
formatRef(i->oprnd1()),
|
||||
formatRef(i->oprnd2()));
|
||||
break;
|
||||
|
||||
|
|
|
@ -181,6 +181,10 @@ namespace nanojit {
|
|||
#define gpn(r) regNames[(r)]
|
||||
#define fpn(r) regNames[(r)]
|
||||
#elif defined(NJ_VERBOSE)
|
||||
// Used for printing native instructions. Like Assembler::outputf(),
|
||||
// but only outputs if LC_Assembly is set. Also prepends the output
|
||||
// with the address of the current native instruction if
|
||||
// LC_NoCodeAddrs is not set.
|
||||
#define asm_output(...) do { \
|
||||
counter_increment(native); \
|
||||
if (_logc->lcbits & LC_Assembly) { \
|
||||
|
@ -190,9 +194,7 @@ namespace nanojit {
|
|||
else \
|
||||
VMPI_memset(outline, (int)' ', 10+3); \
|
||||
sprintf(&outline[13], ##__VA_ARGS__); \
|
||||
Assembler::outputAlign(outline, 35); \
|
||||
_allocator.formatRegisters(outline, _thisfrag); \
|
||||
Assembler::output_asm(outline); \
|
||||
output(); \
|
||||
outputAddr=(_logc->lcbits & LC_NoCodeAddrs) ? false : true; \
|
||||
} \
|
||||
} while (0) /* no semi */
|
||||
|
|
|
@ -1230,9 +1230,6 @@ Assembler::asm_restore(LInsp i, Reservation *, Register r)
|
|||
}
|
||||
}
|
||||
}
|
||||
verbose_only(
|
||||
asm_output(" restore %s",_thisfrag->lirbuf->names->formatRef(i));
|
||||
)
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1283,7 +1280,7 @@ Assembler::asm_load64(LInsp ins)
|
|||
NanoAssert(IsGpReg(rb));
|
||||
freeRsrcOf(ins, false);
|
||||
|
||||
//output("--- load64: Finished register allocation.");
|
||||
//outputf("--- load64: Finished register allocation.");
|
||||
|
||||
if (ARM_VFP && isKnownReg(rr)) {
|
||||
// VFP is enabled and the result will go into a register.
|
||||
|
|
|
@ -592,9 +592,6 @@ namespace nanojit
|
|||
} else {
|
||||
LWZ(r, d, FP);
|
||||
}
|
||||
verbose_only( if (_logc->lcbits & LC_RegAlloc) {
|
||||
outputForEOL(" <= restore %s",
|
||||
_thisfrag->lirbuf->names->formatRef(i)); } )
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ namespace nanojit
|
|||
verbose_only(
|
||||
if (_logc->lcbits & LC_Assembly) {
|
||||
outputf(" %p:",_nIns);
|
||||
output(" patch entry:");
|
||||
outputf(" patch entry:");
|
||||
})
|
||||
NIns *patchEntry = _nIns;
|
||||
|
||||
|
@ -282,9 +282,6 @@ namespace nanojit
|
|||
ADD(FP, L2, r);
|
||||
int32_t d = disp(i);
|
||||
SET32(d, L2);
|
||||
verbose_only(if (_logc->lcbits & LC_RegAlloc) {
|
||||
outputf(" remat %s size %d", _thisfrag->lirbuf->names->formatRef(i), i->size());
|
||||
})
|
||||
}
|
||||
else if (i->isconst()) {
|
||||
if (!i->getArIndex()) {
|
||||
|
@ -299,9 +296,6 @@ namespace nanojit
|
|||
} else {
|
||||
LDSW32(FP, d, r);
|
||||
}
|
||||
verbose_only(if (_logc->lcbits & LC_RegAlloc) {
|
||||
outputf(" restore %s", _thisfrag->lirbuf->names->formatRef(i));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -495,6 +495,10 @@ namespace nanojit
|
|||
|
||||
void Assembler::JMP32(S n, NIns* t) { emit_target32(n,X64_jmp, t); asm_output("jmp %p", t); }
|
||||
|
||||
void Assembler::JMPX(R indexreg, NIns** table) { emitrxb_imm(X64_jmpx, (R)0, indexreg, (Register)5, (int32_t)(uintptr_t)table); asm_output("jmpq [%s*8 + %p]", RQ(indexreg), (void*)table); }
|
||||
|
||||
void Assembler::JMPXB(R indexreg, R tablereg) { emitxb(X64_jmpxb, indexreg, tablereg); asm_output("jmp [%s*8 + %s]", RQ(indexreg), RQ(tablereg)); }
|
||||
|
||||
void Assembler::JO( S n, NIns* t) { emit_target32(n,X64_jo, t); asm_output("jo %p", t); }
|
||||
void Assembler::JE( S n, NIns* t) { emit_target32(n,X64_je, t); asm_output("je %p", t); }
|
||||
void Assembler::JL( S n, NIns* t) { emit_target32(n,X64_jl, t); asm_output("jl %p", t); }
|
||||
|
@ -1239,7 +1243,6 @@ namespace nanojit
|
|||
}
|
||||
|
||||
void Assembler::asm_restore(LIns *ins, Reservation *, Register r) {
|
||||
(void) r;
|
||||
if (ins->isop(LIR_alloc)) {
|
||||
int d = disp(ins);
|
||||
LEAQRM(r, d, FP);
|
||||
|
@ -1270,9 +1273,6 @@ namespace nanojit
|
|||
MOVLRM(r, d, FP);
|
||||
}
|
||||
}
|
||||
verbose_only( if (_logc->lcbits & LC_RegAlloc) {
|
||||
outputForEOL(" <= restore %s",
|
||||
_thisfrag->lirbuf->names->formatRef(ins)); } )
|
||||
}
|
||||
|
||||
void Assembler::asm_cond(LIns *ins) {
|
||||
|
@ -1746,18 +1746,18 @@ namespace nanojit
|
|||
|
||||
void Assembler::asm_jtbl(LIns* ins, NIns** table)
|
||||
{
|
||||
// exclude R12 becuase ESP and R12 cannot be used as an index
|
||||
// exclude R12 because ESP and R12 cannot be used as an index
|
||||
// (index=100 in SIB means "none")
|
||||
Register indexreg = findRegFor(ins->oprnd1(), GpRegs & ~rmask(R12));
|
||||
if (isS32((intptr_t)table)) {
|
||||
// table is in low 2GB or high 2GB, can use absolute addressing
|
||||
// jmpq [indexreg*8 + table]
|
||||
emitrxb_imm(X64_jmpx, (Register)0, indexreg, (Register)5, (int32_t)(uintptr_t)table);
|
||||
JMPX(indexreg, table);
|
||||
} else {
|
||||
// don't use R13 for base because we want to use mod=00, i.e. [index*8+base + 0]
|
||||
Register tablereg = registerAllocTmp(GpRegs & ~(rmask(indexreg)|rmask(R13)));
|
||||
// jmp [indexreg*8 + tablereg]
|
||||
emitxb(X64_jmpxb, indexreg, tablereg);
|
||||
JMPXB(indexreg, tablereg);
|
||||
// tablereg <- #table
|
||||
asm_quad(tablereg, (uint64_t)table);
|
||||
}
|
||||
|
|
|
@ -502,6 +502,8 @@ namespace nanojit
|
|||
void MOVSDMR(Register r1, int d, Register r2);\
|
||||
void JMP8(size_t n, NIns* t);\
|
||||
void JMP32(size_t n, NIns* t);\
|
||||
void JMPX(Register indexreg, NIns** table);\
|
||||
void JMPXB(Register indexreg, Register tablereg);\
|
||||
void JO(size_t n, NIns* t);\
|
||||
void JE(size_t n, NIns* t);\
|
||||
void JL(size_t n, NIns* t);\
|
||||
|
|
|
@ -401,9 +401,6 @@ namespace nanojit
|
|||
uint32_t arg;
|
||||
uint32_t abi_regcount;
|
||||
if (i->isop(LIR_alloc)) {
|
||||
verbose_only( if (_logc->lcbits & LC_RegAlloc) {
|
||||
outputForEOL(" <= remat %s size %d",
|
||||
_thisfrag->lirbuf->names->formatRef(i), i->size()); } )
|
||||
LEA(r, disp(i), FP);
|
||||
}
|
||||
else if (i->isconst()) {
|
||||
|
@ -429,9 +426,6 @@ namespace nanojit
|
|||
}
|
||||
else {
|
||||
int d = findMemFor(i);
|
||||
verbose_only( if (_logc->lcbits & LC_RegAlloc) {
|
||||
outputForEOL(" <= restore %s",
|
||||
_thisfrag->lirbuf->names->formatRef(i)); } )
|
||||
asm_load(d,r);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,31 +43,6 @@ namespace nanojit
|
|||
{
|
||||
#ifdef FEATURE_NANOJIT
|
||||
|
||||
#ifdef NJ_VERBOSE
|
||||
void RegAlloc::formatRegisters(char* s, Fragment *frag)
|
||||
{
|
||||
if (!frag || !frag->lirbuf)
|
||||
return;
|
||||
LirNameMap *names = frag->lirbuf->names;
|
||||
for (Register r = FirstReg; r <= LastReg; r = nextreg(r))
|
||||
{
|
||||
LIns *ins = getActive(r);
|
||||
if (!ins)
|
||||
continue;
|
||||
NanoAssertMsg(!isFree(r), "Coding error; register is both free and active! " );
|
||||
|
||||
if (ins->isop(LIR_param) && ins->paramKind()==1 && r == Assembler::savedRegs[ins->paramArg()]) {
|
||||
// dont print callee-saved regs that arent used
|
||||
continue;
|
||||
}
|
||||
|
||||
s += VMPI_strlen(s);
|
||||
const char* rname = ins->isQuad() ? fpn(r) : gpn(r);
|
||||
VMPI_sprintf(s, " %s(%s)", rname, names->formatRef(ins));
|
||||
}
|
||||
}
|
||||
#endif /* NJ_VERBOSE */
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
uint32_t RegAlloc::countActive()
|
||||
|
|
|
@ -179,8 +179,6 @@ namespace nanojit
|
|||
RegisterMask free;
|
||||
int32_t priority;
|
||||
|
||||
verbose_only( void formatRegisters(char* s, Fragment*); )
|
||||
|
||||
DECLARE_PLATFORM_REGALLOC()
|
||||
};
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче