Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
M
mu-impl-fast
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
40
Issues
40
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
mu
mu-impl-fast
Commits
a9e2bc9b
Commit
a9e2bc9b
authored
Oct 14, 2016
by
qinsoon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[wip] generate spill code. going to copy and insert the code
parent
c15fcff7
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
350 additions
and
99 deletions
+350
-99
mu.iml
mu.iml
+1
-1
src/compiler/backend/arch/x86_64/asm_backend.rs
src/compiler/backend/arch/x86_64/asm_backend.rs
+127
-5
src/compiler/backend/arch/x86_64/codegen.rs
src/compiler/backend/arch/x86_64/codegen.rs
+89
-0
src/compiler/backend/arch/x86_64/mod.rs
src/compiler/backend/arch/x86_64/mod.rs
+1
-0
src/compiler/backend/mod.rs
src/compiler/backend/mod.rs
+2
-0
src/compiler/backend/reg_alloc/mod.rs
src/compiler/backend/reg_alloc/mod.rs
+2
-12
src/compiler/machine_code.rs
src/compiler/machine_code.rs
+5
-0
src/utils/src/vec_utils.rs
src/utils/src/vec_utils.rs
+123
-81
No files found.
mu.iml
View file @
a9e2bc9b
...
...
@@ -11,7 +11,7 @@
</content>
<orderEntry
type=
"inheritedJdk"
/>
<orderEntry
type=
"sourceFolder"
forTests=
"false"
/>
<orderEntry
type=
"library"
name=
"Rust <mu>"
level=
"project"
/>
<orderEntry
type=
"library"
name=
"Cargo <mu>"
level=
"project"
/>
<orderEntry
type=
"library"
name=
"Rust <mu>"
level=
"project"
/>
</component>
</module>
\ No newline at end of file
src/compiler/backend/arch/x86_64/asm_backend.rs
View file @
a9e2bc9b
...
...
@@ -13,7 +13,6 @@ use runtime::ValueLocation;
use
utils
::
vec_utils
;
use
utils
::
string_utils
;
use
ast
::
ptr
::
P
;
use
ast
::
ir
::
*
;
...
...
@@ -84,7 +83,10 @@ impl MachineCode for ASMCode {
}
fn
replace_reg
(
&
mut
self
,
from
:
MuID
,
to
:
MuID
)
{
let
to_reg_tag
:
MuName
=
backend
::
all_regs
()
.get
(
&
to
)
.unwrap
()
.name
()
.unwrap
();
let
to_reg_tag
:
MuName
=
match
backend
::
all_regs
()
.get
(
&
to
)
{
Some
(
reg
)
=>
reg
.name
()
.unwrap
(),
None
=>
panic!
(
"expecting a machine register, but we are required to replace to {}"
,
to
)
};
let
to_reg_string
=
"%"
.to_string
()
+
&
to_reg_tag
;
match
self
.reg_defines
.get
(
&
from
)
{
...
...
@@ -98,19 +100,109 @@ impl MachineCode for ASMCode {
},
None
=>
{}
}
match
self
.reg_uses
.get
(
&
from
)
{
Some
(
uses
)
=>
{
for
loc
in
uses
{
let
ref
mut
inst_to_patch
=
self
.code
[
loc
.line
];
for
i
in
0
..
loc
.len
{
string_utils
::
replace
(
&
mut
inst_to_patch
.code
,
loc
.index
,
&
to_reg_string
,
to_reg_string
.len
());
}
}
}
},
None
=>
{}
}
}
fn
replace_reg_for_inst
(
&
mut
self
,
from
:
MuID
,
to
:
MuID
,
inst
:
usize
)
{
let
to_reg_string
:
MuName
=
match
backend
::
all_regs
()
.get
(
&
to
)
{
Some
(
ref
machine_reg
)
=>
{
let
name
=
machine_reg
.name
()
.unwrap
();
"%"
.to_string
()
+
&
name
},
None
=>
REG_PLACEHOLDER
.clone
()
};
{
let
asm
=
&
mut
self
.code
[
inst
];
// if this reg is defined, replace the define
if
asm
.defines
.contains
(
&
from
)
{
vec_utils
::
remove_value
(
&
mut
asm
.defines
,
from
);
asm
.defines
.push
(
to
);
}
// if this reg is used, replace the use
if
asm
.uses
.contains
(
&
from
)
{
vec_utils
::
remove_value
(
&
mut
asm
.uses
,
from
);
asm
.uses
.push
(
to
);
}
}
// replace the define
// replace code
match
self
.reg_defines
.get
(
&
from
)
{
Some
(
defines
)
=>
{
for
loc
in
defines
{
if
loc
.line
==
inst
{
let
ref
mut
inst_to_patch
=
self
.code
[
loc
.line
];
for
i
in
0
..
loc
.len
{
string_utils
::
replace
(
&
mut
inst_to_patch
.code
,
loc
.index
,
&
to_reg_string
,
to_reg_string
.len
());
}
}
}
},
None
=>
{}
}
// replace info
let
replaced_define
=
match
self
.reg_defines
.get_mut
(
&
from
)
{
Some
(
defines
)
=>
{
Some
(
vec_utils
::
remove_value_if_true
(
defines
,
|
x
|
{
x
.line
==
inst
}))
},
None
=>
None
};
match
replaced_define
{
Some
(
mut
defines
)
=>
{
if
self
.reg_defines
.contains_key
(
&
to
)
{
self
.reg_defines
.get_mut
(
&
to
)
.unwrap
()
.append
(
&
mut
defines
);
}
else
{
self
.reg_defines
.insert
(
to
,
defines
);
}
}
None
=>
{}
};
// replace the use
// replace code
match
self
.reg_uses
.get
(
&
from
)
{
Some
(
uses
)
=>
{
for
loc
in
uses
{
if
loc
.line
==
inst
{
let
ref
mut
inst_to_patch
=
self
.code
[
loc
.line
];
for
i
in
0
..
loc
.len
{
string_utils
::
replace
(
&
mut
inst_to_patch
.code
,
loc
.index
,
&
to_reg_string
,
to_reg_string
.len
());
}
}
}
},
None
=>
{}
}
// replace info
let
replaced_use
=
match
self
.reg_uses
.get_mut
(
&
from
)
{
Some
(
uses
)
=>
{
Some
(
vec_utils
::
remove_value_if_true
(
uses
,
|
x
|
{
x
.line
==
inst
}))
},
None
=>
None
};
match
replaced_use
{
Some
(
mut
uses
)
=>
{
if
self
.reg_uses
.contains_key
(
&
to
)
{
self
.reg_uses
.get_mut
(
&
to
)
.unwrap
()
.append
(
&
mut
uses
);
}
else
{
self
.reg_uses
.insert
(
to
,
uses
);
}
}
None
=>
{}
};
}
fn
set_inst_nop
(
&
mut
self
,
index
:
usize
)
{
// FIXME: changing these info is inefficient - plus we probably do not need to
...
...
@@ -723,7 +815,37 @@ impl CodeGenerator for ASMCodeGen {
ValueLocation
::
Relocatable
(
RegGroup
::
GPR
,
func_end
)
)
}
fn
start_code_sequence
(
&
mut
self
)
{
self
.cur
=
Some
(
Box
::
new
(
ASMCode
{
name
:
"snippet"
.to_string
(),
code
:
vec!
[],
reg_defines
:
HashMap
::
new
(),
reg_uses
:
HashMap
::
new
(),
mem_op_used
:
HashMap
::
new
(),
preds
:
vec!
[],
succs
:
vec!
[],
idx_to_blk
:
HashMap
::
new
(),
blk_to_idx
:
HashMap
::
new
(),
cond_branches
:
HashMap
::
new
(),
branches
:
HashMap
::
new
(),
blocks
:
vec!
[],
block_start
:
HashMap
::
new
(),
block_range
:
HashMap
::
new
(),
block_livein
:
HashMap
::
new
(),
block_liveout
:
HashMap
::
new
()
}));
}
fn
finish_code_sequence
(
&
mut
self
)
->
Box
<
MachineCode
+
Sync
+
Send
>
{
self
.cur
.take
()
.unwrap
()
}
fn
print_cur_code
(
&
self
)
{
println!
(
""
);
...
...
src/compiler/backend/arch/x86_64/codegen.rs
View file @
a9e2bc9b
...
...
@@ -3,10 +3,15 @@ use ast::ir::*;
use
runtime
::
ValueLocation
;
use
compiler
::
machine_code
::
MachineCode
;
use
compiler
::
backend
::
x86_64
::
ASMCodeGen
;
pub
trait
CodeGenerator
{
fn
start_code
(
&
mut
self
,
func_name
:
MuName
)
->
ValueLocation
;
fn
finish_code
(
&
mut
self
,
func_name
:
MuName
)
->
(
Box
<
MachineCode
+
Sync
+
Send
>
,
ValueLocation
);
// generate unnamed sequence of linear code (no branch)
fn
start_code_sequence
(
&
mut
self
);
fn
finish_code_sequence
(
&
mut
self
)
->
Box
<
MachineCode
+
Sync
+
Send
>
;
fn
print_cur_code
(
&
self
);
...
...
@@ -66,3 +71,87 @@ pub trait CodeGenerator {
fn
emit_push_imm32
(
&
mut
self
,
src
:
i32
);
fn
emit_pop_r64
(
&
mut
self
,
dest
:
&
P
<
Value
>
);
}
use
std
::
collections
::
HashMap
;
use
compiler
::
machine_code
::
CompiledFunction
;
use
vm
::
VM
;
#[cfg(feature
=
"aot"
)]
pub
fn
spill_rewrite
(
spills
:
&
HashMap
<
MuID
,
P
<
Value
>>
,
func
:
&
mut
MuFunctionVersion
,
cf
:
&
mut
CompiledFunction
,
vm
:
&
VM
)
{
// record code and their insertion point, so we can do the copy/insertion all at once
let
mut
spill_code_before
:
HashMap
<
usize
,
Vec
<
Box
<
MachineCode
>>>
=
HashMap
::
new
();
let
mut
spill_code_after
:
HashMap
<
usize
,
Vec
<
Box
<
MachineCode
>>>
=
HashMap
::
new
();
// iterate through all instructions
for
i
in
0
..
cf
.mc
()
.number_of_insts
()
{
// find use of any register that gets spilled
{
let
reg_uses
=
cf
.mc
()
.get_inst_reg_uses
(
i
)
.to_vec
();
for
reg
in
reg_uses
{
if
spills
.contains_key
(
&
reg
)
{
// a register used here is spilled
let
spill_mem
=
spills
.get
(
&
reg
)
.unwrap
();
// generate a random new temporary
let
temp_ty
=
func
.context
.get_value
(
reg
)
.unwrap
()
.ty
()
.clone
();
let
temp
=
func
.new_ssa
(
vm
.next_id
(),
temp_ty
)
.clone_value
();
// generate a load
let
code
=
{
let
mut
codegen
=
ASMCodeGen
::
new
();
codegen
.start_code_sequence
();
codegen
.emit_mov_r64_mem64
(
&
temp
,
spill_mem
);
codegen
.finish_code_sequence
()
};
// record that this load will be inserted at i
if
spill_code_before
.contains_key
(
&
i
)
{
spill_code_before
.get_mut
(
&
i
)
.unwrap
()
.push
(
code
);
}
else
{
spill_code_before
.insert
(
i
,
vec!
[
code
]);
}
// replace register reg with temp
cf
.mc_mut
()
.replace_reg_for_inst
(
reg
,
temp
.id
(),
i
);
}
}
}
// fine define of any register that gets spilled
{
let
reg_defines
=
cf
.mc
()
.get_inst_reg_defines
(
i
)
.to_vec
();
for
reg
in
reg_defines
{
if
spills
.contains_key
(
&
reg
)
{
let
spill_mem
=
spills
.get
(
&
reg
)
.unwrap
();
let
temp_ty
=
func
.context
.get_value
(
reg
)
.unwrap
()
.ty
()
.clone
();
let
temp
=
func
.new_ssa
(
vm
.next_id
(),
temp_ty
)
.clone_value
();
let
code
=
{
let
mut
codegen
=
ASMCodeGen
::
new
();
codegen
.start_code_sequence
();
codegen
.emit_mov_mem64_r64
(
spill_mem
,
&
temp
);
codegen
.finish_code_sequence
()
};
if
spill_code_after
.contains_key
(
&
i
)
{
spill_code_after
.get_mut
(
&
i
)
.unwrap
()
.push
(
code
);
}
else
{
spill_code_after
.insert
(
i
,
vec!
[
code
]);
}
cf
.mc_mut
()
.replace_reg_for_inst
(
reg
,
temp
.id
(),
i
);
}
}
}
}
// copy and insert the code
unimplemented!
()
}
\ No newline at end of file
src/compiler/backend/arch/x86_64/mod.rs
View file @
a9e2bc9b
...
...
@@ -5,6 +5,7 @@ pub mod inst_sel;
mod
codegen
;
pub
use
compiler
::
backend
::
x86_64
::
codegen
::
CodeGenerator
;
pub
use
compiler
::
backend
::
x86_64
::
codegen
::
spill_rewrite
;
mod
asm_backend
;
pub
use
compiler
::
backend
::
x86_64
::
asm_backend
::
ASMCodeGen
;
...
...
src/compiler/backend/mod.rs
View file @
a9e2bc9b
...
...
@@ -36,6 +36,8 @@ pub use compiler::backend::x86_64::is_callee_saved;
pub
use
compiler
::
backend
::
x86_64
::
emit_code
;
#[cfg(target_arch
=
"x86_64"
)]
pub
use
compiler
::
backend
::
x86_64
::
emit_context
;
#[cfg(target_arch
=
"x86_64"
)]
pub
use
compiler
::
backend
::
x86_64
::
spill_rewrite
;
// ARM
...
...
src/compiler/backend/reg_alloc/mod.rs
View file @
a9e2bc9b
...
...
@@ -8,6 +8,7 @@ use compiler::CompilerPass;
use
compiler
::
machine_code
::
CompiledFunction
;
use
compiler
::
PassExecutionResult
;
use
compiler
::
backend
::
init_machine_regs_for_func
;
use
compiler
::
backend
;
use
std
::
collections
::
HashMap
;
...
...
@@ -65,7 +66,7 @@ impl RegisterAllocation {
spilled_mem
.insert
(
*
reg_id
,
mem
);
}
self
.
spill_rewrite
(
&
spilled_mem
,
func
,
&
mut
cf
,
vm
);
backend
::
spill_rewrite
(
&
spilled_mem
,
func
,
&
mut
cf
,
vm
);
return
Err
(
RegAllocFailure
::
FailedForSpilling
);
}
...
...
@@ -93,17 +94,6 @@ impl RegisterAllocation {
Ok
(())
}
#[cfg(feature
=
"aot"
)]
fn
spill_rewrite
(
&
mut
self
,
spills
:
&
HashMap
<
MuID
,
P
<
Value
>>
,
func
:
&
mut
MuFunctionVersion
,
compiled_func
:
&
mut
CompiledFunction
,
vm
:
&
VM
)
{
unimplemented!
()
}
}
impl
CompilerPass
for
RegisterAllocation
{
...
...
src/compiler/machine_code.rs
View file @
a9e2bc9b
...
...
@@ -118,5 +118,10 @@ pub trait MachineCode {
// functions for rewrite
fn
replace_reg
(
&
mut
self
,
from
:
MuID
,
to
:
MuID
);
fn
replace_reg_for_inst
(
&
mut
self
,
from
:
MuID
,
to
:
MuID
,
inst
:
usize
);
fn
set_inst_nop
(
&
mut
self
,
index
:
usize
);
}
pub
trait
MachineInst
{
}
\ No newline at end of file
src/utils/src/vec_utils.rs
View file @
a9e2bc9b
use
std
::
fmt
;
pub
fn
is_identical_to_str_ignore_order
<
T
:
Ord
+
fmt
::
Display
+
Clone
,
Q
:
Ord
+
fmt
::
Display
+
Clone
>
(
vec
:
&
Vec
<
T
>
,
mut
expect
:
Vec
<
Q
>
)
->
bool
{
let
mut
vec_copy
=
vec
.to_vec
();
vec_copy
.sort
();
expect
.sort
();
let
a
=
as_str
(
&
vec_copy
);
let
b
=
as_str
(
&
expect
);
a
==
b
use
std
::
fmt
;
pub
fn
is_identical_to_str_ignore_order
<
T
:
Ord
+
fmt
::
Display
+
Clone
,
Q
:
Ord
+
fmt
::
Display
+
Clone
>
(
vec
:
&
Vec
<
T
>
,
mut
expect
:
Vec
<
Q
>
)
->
bool
{
let
mut
vec_copy
=
vec
.to_vec
();
vec_copy
.sort
();
expect
.sort
();
let
a
=
as_str
(
&
vec_copy
);
let
b
=
as_str
(
&
expect
);
a
==
b
}
pub
fn
is_identical_ignore_order
<
T
:
Ord
+
Clone
>
(
vec
:
&
Vec
<
T
>
,
vec2
:
&
Vec
<
T
>
)
->
bool
{
if
vec
.len
()
!=
vec2
.len
()
{
return
false
;
}
pub
fn
is_identical_ignore_order
<
T
:
Ord
+
Clone
>
(
vec
:
&
Vec
<
T
>
,
vec2
:
&
Vec
<
T
>
)
->
bool
{
if
vec
.len
()
!=
vec2
.len
()
{
let
mut
vec
=
vec
.to_vec
();
let
mut
vec2
=
vec2
.to_vec
();
vec
.sort
();
vec2
.sort
();
for
i
in
0
..
vec
.len
()
{
if
vec
[
i
]
!=
vec2
[
i
]
{
return
false
;
}
let
mut
vec
=
vec
.to_vec
();
let
mut
vec2
=
vec2
.to_vec
();
vec
.sort
();
vec2
.sort
();
for
i
in
0
..
vec
.len
()
{
if
vec
[
i
]
!=
vec2
[
i
]
{
return
false
;
}
}
return
true
;
}
pub
fn
as_str
<
T
:
fmt
::
Display
>
(
vec
:
&
Vec
<
T
>
)
->
String
{
let
mut
ret
=
String
::
new
();
for
i
in
0
..
vec
.len
()
{
ret
.push_str
(
format!
(
"{}"
,
vec
[
i
])
.as_str
());
if
i
!=
vec
.len
()
-
1
{
ret
.push_str
(
", "
);
}
return
true
;
}
pub
fn
as_str
<
T
:
fmt
::
Display
>
(
vec
:
&
Vec
<
T
>
)
->
String
{
let
mut
ret
=
String
::
new
();
for
i
in
0
..
vec
.len
()
{
ret
.push_str
(
format!
(
"{}"
,
vec
[
i
])
.as_str
());
if
i
!=
vec
.len
()
-
1
{
ret
.push_str
(
", "
);
}
ret
}
ret
}
pub
fn
add_all
<
T
:
Copy
+
PartialEq
>
(
vec
:
&
mut
Vec
<
T
>
,
vec2
:
&
Vec
<
T
>
)
->
bool
{
let
mut
is_changed
=
false
;
pub
fn
add_all
<
T
:
Copy
+
PartialEq
>
(
vec
:
&
mut
Vec
<
T
>
,
vec2
:
&
Vec
<
T
>
)
->
bool
{
let
mut
is_changed
=
false
;
for
i
in
vec2
.iter
()
{
if
!
vec
.contains
(
i
)
{
vec
.push
(
*
i
);
is_changed
=
true
;
}
for
i
in
vec2
.iter
()
{
if
!
vec
.contains
(
i
)
{
vec
.push
(
*
i
);
is_changed
=
true
;
}
}
is_changed
}
is_changed
pub
fn
add_unique
<
T
:
PartialEq
>
(
vec
:
&
mut
Vec
<
T
>
,
val
:
T
)
{
if
!
vec
.contains
(
&
val
)
{
vec
.push
(
val
);
}
pub
fn
add_unique
<
T
:
PartialEq
>
(
vec
:
&
mut
Vec
<
T
>
,
val
:
T
)
{
if
!
vec
.contains
(
&
val
)
{
vec
.push
(
val
);
}
}
pub
fn
append_unique
<
T
:
PartialEq
>
(
vec
:
&
mut
Vec
<
T
>
,
vec2
:
&
mut
Vec
<
T
>
)
{
while
!
vec2
.is_empty
()
{
let
val
=
vec2
.pop
()
.unwrap
();
add_unique
(
vec
,
val
);
}
pub
fn
append_unique
<
T
:
PartialEq
>
(
vec
:
&
mut
Vec
<
T
>
,
vec2
:
&
mut
Vec
<
T
>
)
{
while
!
vec2
.is_empty
()
{
let
val
=
vec2
.pop
()
.unwrap
();
add_unique
(
vec
,
val
);
}
}
pub
fn
append_clone_unique
<
T
:
PartialEq
+
Clone
>
(
vec
:
&
mut
Vec
<
T
>
,
vec2
:
&
Vec
<
T
>
)
{
for
ele
in
vec2
{
add_unique
(
vec
,
ele
.clone
());
}
pub
fn
append_clone_unique
<
T
:
PartialEq
+
Clone
>
(
vec
:
&
mut
Vec
<
T
>
,
vec2
:
&
Vec
<
T
>
)
{
for
ele
in
vec2
{
add_unique
(
vec
,
ele
.clone
());
}
pub
fn
find_value
<
T
:
PartialEq
>
(
vec
:
&
Vec
<
T
>
,
val
:
T
)
->
Option
<
usize
>
{
for
i
in
0
..
vec
.len
()
{
if
vec
[
i
]
==
val
{
return
Some
(
i
);
}
}
pub
fn
find_value
<
T
:
PartialEq
>
(
vec
:
&
Vec
<
T
>
,
val
:
T
)
->
Option
<
usize
>
{
for
i
in
0
..
vec
.len
()
{
if
vec
[
i
]
==
val
{
return
Some
(
i
);
}
}
None
}
None
pub
fn
remove_value
<
T
:
PartialEq
>
(
vec
:
&
mut
Vec
<
T
>
,
val
:
T
)
{
match
find_value
(
vec
,
val
)
{
Some
(
index
)
=>
{
vec
.remove
(
index
);},
None
=>
{}
// do nothing
}
}
pub
fn
remove_value_if_true
<
T
,
F
>
(
vec
:
&
mut
Vec
<
T
>
,
cond
:
F
)
->
Vec
<
T
>
where
F
:
Fn
(
&
T
)
->
bool
{
let
mut
new_vec
=
vec!
[];
let
mut
removed
=
vec!
[];
pub
fn
remove_value
<
T
:
PartialEq
>
(
vec
:
&
mut
Vec
<
T
>
,
val
:
T
)
{
match
find_value
(
vec
,
val
)
{
Some
(
index
)
=>
{
vec
.remove
(
index
);},
None
=>
{}
// do nothing
while
!
vec
.is_empty
()
{
let
val
=
vec
.pop
()
.unwrap
();
if
cond
(
&
val
)
{
// true - remove
removed
.push
(
val
);
}
else
{
new_vec
.push
(
val
);
}
}
pub
fn
map
<
T
,
Q
,
F
>
(
vec
:
&
Vec
<
T
>
,
map_func
:
F
)
->
Vec
<
Q
>
where
F
:
Fn
(
&
T
)
->
Q
{
let
mut
ret
=
vec!
[];
for
t
in
vec
{
ret
.push
(
map_func
(
t
));
vec
.append
(
&
mut
new_vec
);
removed
}
pub
fn
remove_value_if_false
<
T
,
F
>
(
vec
:
&
mut
Vec
<
T
>
,
cond
:
F
)
->
Vec
<
T
>
where
F
:
Fn
(
&
T
)
->
bool
{
let
mut
new_vec
=
vec!
[];
let
mut
removed
=
vec!
[];
while
!
vec
.is_empty
()
{
let
val
=
vec
.pop
()
.unwrap
();
if
cond
(
&
val
)
{
new_vec
.push
(
val
)
}
else
{
// false - remove
removed
.push
(
val
)
}
ret
}
\ No newline at end of file
}
vec
.append
(
&
mut
new_vec
);
removed
}
pub
fn
map
<
T
,
Q
,
F
>
(
vec
:
&
Vec
<
T
>
,
map_func
:
F
)
->
Vec
<
Q
>
where
F
:
Fn
(
&
T
)
->
Q
{
let
mut
ret
=
vec!
[];
for
t
in
vec
{
ret
.push
(
map_func
(
t
));
}
ret
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment